unparser 0.4.3 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
  unparser
2
2
  ========
3
3
 
4
- [![Build Status](https://secure.travis-ci.org/mbj/unparser.png?branch=master)](http://travis-ci.org/mbj/unparser)
5
- [![Dependency Status](https://gemnasium.com/mbj/unparser.png)](https://gemnasium.com/mbj/unparser)
6
- [![Code Climate](https://codeclimate.com/github/mbj/unparser.png)](https://codeclimate.com/github/mbj/unparser)
4
+ [![CI](https://github.com/mbj/unparser/workflows/CI/badge.svg)
7
5
  [![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser)
8
6
 
9
7
  Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser).
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 13
3
- total_score: 645
3
+ total_score: 638
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
3
  require 'abstract_type'
5
- require 'procto'
4
+ require 'anima'
6
5
  require 'concord'
6
+ require 'diff/lcs'
7
+ require 'diff/lcs/hunk'
8
+ require 'mprelude'
7
9
  require 'parser/current'
10
+ require 'procto'
11
+ require 'set'
8
12
 
9
13
  # Library namespace
10
14
  module Unparser
@@ -40,11 +44,22 @@ module Unparser
40
44
  #
41
45
  # @param [String] source
42
46
  #
43
- # @return [Parser::AST::Node]
47
+ # @return [Parser::AST::Node, nil]
44
48
  def self.parse(source)
45
49
  parser.parse(buffer(source))
46
50
  end
47
51
 
52
+ # Parse string into either syntax error or AST
53
+ #
54
+ # @param [String] source
55
+ #
56
+ # @return [MPrelude::Either<Parser::SyntaxError, (Parser::ASTNode, nil)>]
57
+ def self.parse_either(source)
58
+ MPrelude::Either.wrap_error(Parser::SyntaxError) do
59
+ parser.parse(buffer(source))
60
+ end
61
+ end
62
+
48
63
  # Parse string into AST, with comments
49
64
  #
50
65
  # @param [String] source
@@ -100,6 +115,8 @@ require 'unparser/constants'
100
115
  require 'unparser/dsl'
101
116
  require 'unparser/ast'
102
117
  require 'unparser/ast/local_variable_scope'
118
+ require 'unparser/diff'
119
+ require 'unparser/color'
103
120
  require 'unparser/emitter'
104
121
  require 'unparser/emitter/literal'
105
122
  require 'unparser/emitter/literal/primitive'
@@ -152,5 +169,6 @@ require 'unparser/emitter/resbody'
152
169
  require 'unparser/emitter/ensure'
153
170
  require 'unparser/emitter/index'
154
171
  require 'unparser/emitter/lambda'
172
+ require 'unparser/validation'
155
173
  # make it easy for zombie
156
174
  require 'unparser/finalize'
@@ -50,7 +50,7 @@ module Unparser
50
50
 
51
51
  # Return local variables that get assigned in scope
52
52
  #
53
- # @param [Parser::AST::Node]
53
+ # @param [Parser::AST::Node] node
54
54
  #
55
55
  # @return [Set<Symbol>]
56
56
  #
@@ -56,8 +56,8 @@ module Unparser
56
56
 
57
57
  # Test if local variables where first assigned in body and read by conditional
58
58
  #
59
- # @param [Parser::AST::Node] conditional
60
59
  # @param [Parser::AST::Node] body
60
+ # @param [Parser::AST::Node] condition
61
61
  #
62
62
  # @api private
63
63
  #
@@ -78,16 +78,16 @@ module Unparser
78
78
 
79
79
  # Match node
80
80
  #
81
- # @param [Parser::AST::Node] node
81
+ # @param [Parser::AST::Node] needle
82
82
  # if block given
83
83
  #
84
84
  # @return [Boolean]
85
85
  #
86
86
  # @api private
87
87
  #
88
- def match(neddle)
88
+ def match(needle)
89
89
  @items.each do |node, current, before|
90
- return yield(current, before) if node.equal?(neddle)
90
+ return yield(current, before) if node.equal?(needle)
91
91
  end
92
92
  false
93
93
  end
@@ -149,7 +149,7 @@ module Unparser
149
149
 
150
150
  # Visit node and record local variable state
151
151
  #
152
- # @param [Parser::AST::Node]
152
+ # @param [Parser::AST::Node] node
153
153
  #
154
154
  # @return [undefined]
155
155
  #
@@ -168,7 +168,7 @@ module Unparser
168
168
 
169
169
  # Record local variable state
170
170
  #
171
- # @param [Parser::AST::Node]
171
+ # @param [Parser::AST::Node] node
172
172
  #
173
173
  # @return [undefined]
174
174
  #
@@ -2,12 +2,6 @@
2
2
 
3
3
  require 'unparser'
4
4
  require 'optparse'
5
- require 'diff/lcs'
6
- require 'diff/lcs/hunk'
7
-
8
- require 'unparser/cli/source'
9
- require 'unparser/cli/differ'
10
- require 'unparser/cli/color'
11
5
 
12
6
  module Unparser
13
7
  # Unparser CLI implementation
@@ -19,6 +13,36 @@ module Unparser
19
13
  EXIT_SUCCESS = 0
20
14
  EXIT_FAILURE = 1
21
15
 
16
+ class Target
17
+ include AbstractType
18
+
19
+ # Path target
20
+ class Path < self
21
+ include Concord.new(:path)
22
+
23
+ # Validation for this target
24
+ #
25
+ # @return [Validation]
26
+ def validation
27
+ Validation.from_path(path)
28
+ end
29
+ end
30
+
31
+ # String target
32
+ class String
33
+ include Concord.new(:string)
34
+
35
+ # Validation for this target
36
+ #
37
+ # @return [Validation]
38
+ def validation
39
+ Validation.from_string(string)
40
+ end
41
+ end # String
42
+ end # Target
43
+
44
+ private_constant(*constants(false))
45
+
22
46
  # Run CLI
23
47
  #
24
48
  # @param [Array<String>] arguments
@@ -42,8 +66,8 @@ module Unparser
42
66
  #
43
67
  # ignore :reek:TooManyStatements
44
68
  def initialize(arguments)
45
- @sources = []
46
- @ignore = Set.new
69
+ @ignore = Set.new
70
+ @targets = []
47
71
 
48
72
  @success = true
49
73
  @fail_fast = false
@@ -54,7 +78,7 @@ module Unparser
54
78
  end
55
79
 
56
80
  opts.parse!(arguments).each do |name|
57
- @sources.concat(sources(name))
81
+ @targets.concat(targets(name))
58
82
  end
59
83
  end
60
84
 
@@ -71,16 +95,16 @@ module Unparser
71
95
  builder.banner = 'usage: unparse [options] FILE [FILE]'
72
96
  builder.separator('')
73
97
  builder.on('-e', '--evaluate SOURCE') do |source|
74
- @sources << Source::String.new(source)
98
+ @targets << Target::String.new(source)
75
99
  end
76
- builder.on('--start-with FILE') do |file|
77
- @start_with = sources(file).first
100
+ builder.on('--start-with FILE') do |path|
101
+ @start_with = targets(path).first
78
102
  end
79
103
  builder.on('-v', '--verbose') do
80
104
  @verbose = true
81
105
  end
82
106
  builder.on('--ignore FILE') do |file|
83
- @ignore.merge(sources(file))
107
+ @ignore.merge(targets(file))
84
108
  end
85
109
  builder.on('--fail-fast') do
86
110
  @fail_fast = true
@@ -94,10 +118,8 @@ module Unparser
94
118
  # @api private
95
119
  #
96
120
  def exit_status
97
- effective_sources.each do |source|
98
- next if @ignore.include?(source)
99
-
100
- process_source(source)
121
+ effective_targets.each do |target|
122
+ process_target(target)
101
123
  break if @fail_fast && !@success
102
124
  end
103
125
 
@@ -106,66 +128,64 @@ module Unparser
106
128
 
107
129
  private
108
130
 
109
- # Process source
131
+ # Process target
110
132
  #
111
- # @param [CLI::Source]
133
+ # @param [Target] target
112
134
  #
113
135
  # @return [undefined]
114
136
  #
115
137
  # @api private
116
138
  #
117
- def process_source(source)
118
- if source.success?
119
- puts source.report if @verbose
120
- puts "Success: #{source.identification}"
139
+ def process_target(target)
140
+ validation = target.validation
141
+ if validation.success?
142
+ puts validation.report if @verbose
143
+ puts "Success: #{validation.identification}"
121
144
  else
122
- puts source.report
123
- puts "Error: #{source.identification}"
145
+ puts validation.report
146
+ puts "Error: #{validation.identification}"
124
147
  @success = false
125
148
  end
126
149
  end
127
150
 
128
- # Return effective sources
151
+ # Return effective targets
129
152
  #
130
- # @return [Enumerable<CLI::Source>]
153
+ # @return [Enumerable<Target>]
131
154
  #
132
155
  # @api private
133
156
  #
134
- def effective_sources
157
+ def effective_targets
135
158
  if @start_with
136
159
  reject = true
137
- @sources.reject do |source|
138
- if reject && source.eql?(@start_with)
160
+ @targets.reject do |targets|
161
+ if reject && targets.eql?(@start_with)
139
162
  reject = false
140
163
  end
141
164
 
142
165
  reject
143
166
  end
144
167
  else
145
- @sources
146
- end
168
+ @targets
169
+ end.reject(&@ignore.method(:include?))
147
170
  end
148
171
 
149
- # Return sources for file name
172
+ # Return targets for file name
150
173
  #
151
174
  # @param [String] file_name
152
175
  #
153
- # @return [Enumerable<CLI::Source>]
176
+ # @return [Enumerable<Target>]
154
177
  #
155
178
  # @api private
156
179
  #
157
180
  # ignore :reek:UtilityFunction
158
- def sources(file_name)
159
- files =
160
- if File.directory?(file_name)
161
- Dir.glob(File.join(file_name, '**/*.rb')).sort
162
- elsif File.file?(file_name)
163
- [file_name]
164
- else
165
- Dir.glob(file_name).sort
166
- end
167
-
168
- files.map(&Source::File.method(:new))
181
+ def targets(file_name)
182
+ if File.directory?(file_name)
183
+ Dir.glob(File.join(file_name, '**/*.rb')).sort
184
+ elsif File.file?(file_name)
185
+ [file_name]
186
+ else
187
+ Dir.glob(file_name).sort
188
+ end.map { |file| Target::Path.new(Pathname.new(file)) }
169
189
  end
170
190
 
171
191
  end # CLI
@@ -10,9 +10,6 @@ module Unparser
10
10
  # @param [String] text
11
11
  #
12
12
  # @return [String]
13
- #
14
- # @api private
15
- #
16
13
  def format(text)
17
14
  "\e[#{code}m#{text}\e[0m"
18
15
  end
@@ -25,9 +22,6 @@ module Unparser
25
22
  #
26
23
  # @return [String]
27
24
  # the argument string
28
- #
29
- # @api private
30
- #
31
25
  def format(text)
32
26
  text
33
27
  end
@@ -37,16 +31,12 @@ module Unparser
37
31
  # Initialize null color
38
32
  #
39
33
  # @return [undefined]
40
- #
41
- # @api private
42
- #
43
34
  def initialize; end
44
35
 
45
36
  end.new
46
37
 
47
38
  RED = Color.new(31)
48
39
  GREEN = Color.new(32)
49
- BLUE = Color.new(34)
50
40
 
51
41
  end # Color
52
42
  end # Unparser
@@ -11,7 +11,7 @@ module Unparser
11
11
 
12
12
  # Return frozen symbol set from enumerable
13
13
  #
14
- # @param [Enumerable]
14
+ # @param [Enumerable] enumerable
15
15
  #
16
16
  # @return [Set<Symbol>]
17
17
  #
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # Class to create diffs from source code
5
+ class Diff
6
+ include Adamantium::Flat, Concord.new(:old, :new)
7
+
8
+ ADDITION = '+'
9
+ DELETION = '-'
10
+ NEWLINE = "\n"
11
+
12
+ # Unified source diff between old and new
13
+ #
14
+ # @return [String]
15
+ # if there is exactly one diff
16
+ #
17
+ # @return [nil]
18
+ # otherwise
19
+ def diff
20
+ return if diffs.empty?
21
+
22
+ minimized_hunk.diff(:unified) + NEWLINE
23
+ end
24
+ memoize :diff
25
+
26
+ # Colorized unified source diff between old and new
27
+ #
28
+ # @return [String]
29
+ # if there is a diff
30
+ #
31
+ # @return [nil]
32
+ # otherwise
33
+ def colorized_diff
34
+ return unless diff
35
+
36
+ diff.lines.map(&self.class.method(:colorize_line)).join
37
+ end
38
+ memoize :colorized_diff
39
+
40
+ # Build new object from source strings
41
+ #
42
+ # @param [String] old
43
+ # @param [String] new
44
+ #
45
+ # @return [Diff]
46
+ def self.build(old, new)
47
+ new(lines(old), lines(new))
48
+ end
49
+
50
+ # Break up source into lines
51
+ #
52
+ # @param [String] source
53
+ #
54
+ # @return [Array<String>]
55
+ def self.lines(source)
56
+ source.lines.map(&:chomp)
57
+ end
58
+ private_class_method :lines
59
+
60
+ private
61
+
62
+ # Diffs between old and new
63
+ #
64
+ # @return [Array<Array>]
65
+ def diffs
66
+ ::Diff::LCS.diff(old, new)
67
+ end
68
+
69
+ # Raw diff-lcs hunks
70
+ #
71
+ # @return [Array<Diff::LCS::Hunk>]
72
+ def hunks
73
+ diffs.map do |diff|
74
+ ::Diff::LCS::Hunk.new(old.map(&:dup), new, diff, max_length, 0)
75
+ end
76
+ end
77
+
78
+ # Minimized hunk
79
+ #
80
+ # @return Diff::LCS::Hunk
81
+ def minimized_hunk
82
+ head, *tail = hunks
83
+
84
+ tail.reduce(head) do |left, right|
85
+ right.merge(left)
86
+ right
87
+ end
88
+ end
89
+
90
+ # Max length of source line in new and old
91
+ #
92
+ # @return [Integer]
93
+ def max_length
94
+ [old, new].map(&:length).max
95
+ end
96
+
97
+ # Colorized a unified diff line
98
+ #
99
+ # @param [String] line
100
+ #
101
+ # @return [String]
102
+ def self.colorize_line(line)
103
+ case line[0]
104
+ when ADDITION
105
+ Color::GREEN
106
+ when DELETION
107
+ Color::RED
108
+ else
109
+ Color::NONE
110
+ end.format(line)
111
+ end
112
+ private_class_method :colorize_line
113
+
114
+ end # Diff
115
+ end # Unparser