unparser 0.4.3 → 0.4.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/.github/workflows/ci.yml +90 -0
- data/.rubocop.yml +122 -5
- data/Changelog.md +24 -0
- data/Gemfile +3 -5
- data/Gemfile.lock +55 -122
- data/README.md +1 -3
- data/config/flay.yml +1 -1
- data/lib/unparser.rb +21 -3
- data/lib/unparser/ast.rb +1 -1
- data/lib/unparser/ast/local_variable_scope.rb +6 -6
- data/lib/unparser/cli.rb +65 -45
- data/lib/unparser/{cli/color.rb → color.rb} +0 -10
- data/lib/unparser/constants.rb +1 -1
- data/lib/unparser/diff.rb +115 -0
- data/lib/unparser/dsl.rb +1 -1
- data/lib/unparser/emitter.rb +4 -5
- data/lib/unparser/emitter/argument.rb +9 -13
- data/lib/unparser/emitter/literal/primitive.rb +1 -1
- data/lib/unparser/emitter/literal/range.rb +1 -1
- data/lib/unparser/node_helpers.rb +4 -2
- data/lib/unparser/preprocessor.rb +1 -1
- data/lib/unparser/validation.rb +149 -0
- data/spec/integration/unparser/corpus_spec.rb +33 -19
- data/spec/integrations.yml +6 -1
- data/spec/spec_helper.rb +26 -4
- data/spec/unit/unparser/color_spec.rb +40 -0
- data/spec/unit/unparser/diff_spec.rb +189 -0
- data/spec/unit/unparser/validation_spec.rb +327 -0
- data/spec/unit/unparser_spec.rb +88 -9
- data/unparser.gemspec +12 -7
- metadata +104 -29
- data/.circleci/config.yml +0 -41
- data/config/rubocop.yml +0 -122
- data/lib/unparser/cli/differ.rb +0 -152
- data/lib/unparser/cli/source.rb +0 -267
data/lib/unparser/dsl.rb
CHANGED
data/lib/unparser/emitter.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Unparser
|
4
|
+
UnknownNodeError = Class.new(ArgumentError)
|
4
5
|
|
5
6
|
# Emitter base class
|
6
7
|
#
|
@@ -79,7 +80,7 @@ module Unparser
|
|
79
80
|
|
80
81
|
# Register emitter for type
|
81
82
|
#
|
82
|
-
# @param [Symbol]
|
83
|
+
# @param [Symbol] types
|
83
84
|
#
|
84
85
|
# @return [undefined]
|
85
86
|
#
|
@@ -116,7 +117,7 @@ module Unparser
|
|
116
117
|
def self.emitter(node, parent)
|
117
118
|
type = node.type
|
118
119
|
klass = REGISTRY.fetch(type) do
|
119
|
-
raise
|
120
|
+
raise UnknownNodeError, "Unknown node type: #{type.inspect}"
|
120
121
|
end
|
121
122
|
klass.new(node, parent)
|
122
123
|
end
|
@@ -249,7 +250,6 @@ module Unparser
|
|
249
250
|
# Emit delimited body
|
250
251
|
#
|
251
252
|
# @param [Enumerable<Parser::AST::Node>] nodes
|
252
|
-
# @param [String] delimiter
|
253
253
|
#
|
254
254
|
# @return [undefined]
|
255
255
|
#
|
@@ -262,7 +262,6 @@ module Unparser
|
|
262
262
|
# Emit delimited body
|
263
263
|
#
|
264
264
|
# @param [Enumerable<Parser::AST::Node>] nodes
|
265
|
-
# @param [String] delimiter
|
266
265
|
#
|
267
266
|
# @return [undefined]
|
268
267
|
#
|
@@ -432,7 +431,7 @@ module Unparser
|
|
432
431
|
|
433
432
|
# Emit non nil body
|
434
433
|
#
|
435
|
-
# @param [Parser::AST::Node]
|
434
|
+
# @param [Parser::AST::Node] body
|
436
435
|
#
|
437
436
|
# @return [undefined]
|
438
437
|
#
|
@@ -240,7 +240,7 @@ module Unparser
|
|
240
240
|
|
241
241
|
handle :procarg0
|
242
242
|
|
243
|
-
|
243
|
+
PARENS = %i[restarg mlhs].freeze
|
244
244
|
|
245
245
|
private
|
246
246
|
|
@@ -251,22 +251,18 @@ module Unparser
|
|
251
251
|
# @api private
|
252
252
|
#
|
253
253
|
def dispatch
|
254
|
-
if
|
255
|
-
|
254
|
+
if needs_parens?
|
255
|
+
parentheses do
|
256
|
+
delimited(children)
|
257
|
+
end
|
256
258
|
else
|
257
|
-
|
259
|
+
delimited(children)
|
258
260
|
end
|
259
261
|
end
|
260
262
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
#
|
265
|
-
# @api private
|
266
|
-
#
|
267
|
-
def emit_multiple_children
|
268
|
-
parentheses do
|
269
|
-
delimited(children)
|
263
|
+
def needs_parens?
|
264
|
+
children.length > 1 || children.any? do |node|
|
265
|
+
PARENS.include?(node.type)
|
270
266
|
end
|
271
267
|
end
|
272
268
|
end
|
@@ -5,7 +5,8 @@ module Unparser
|
|
5
5
|
|
6
6
|
# Helper for building nodes
|
7
7
|
#
|
8
|
-
# @param [Symbol]
|
8
|
+
# @param [Symbol] type
|
9
|
+
# @param [Parser::AST::Node] children
|
9
10
|
#
|
10
11
|
# @return [Parser::AST::Node]
|
11
12
|
#
|
@@ -19,9 +20,10 @@ module Unparser
|
|
19
20
|
|
20
21
|
# Helper for building nodes
|
21
22
|
#
|
22
|
-
# @param [Symbol]
|
23
|
+
# @param [Symbol] type
|
23
24
|
#
|
24
25
|
# @return [Parser::AST::Node]
|
26
|
+
# @param [Array] children
|
25
27
|
#
|
26
28
|
# @api private
|
27
29
|
#
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
# Validation of unparser results
|
5
|
+
#
|
6
|
+
# ignore :reek:TooManyMethods
|
7
|
+
class Validation
|
8
|
+
include Adamantium::Flat, Anima.new(
|
9
|
+
:generated_node,
|
10
|
+
:generated_source,
|
11
|
+
:identification,
|
12
|
+
:original_node,
|
13
|
+
:original_source
|
14
|
+
)
|
15
|
+
|
16
|
+
# Test if source could be unparsed successfully
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
#
|
22
|
+
def success?
|
23
|
+
[
|
24
|
+
original_source,
|
25
|
+
original_node,
|
26
|
+
generated_source,
|
27
|
+
generated_node
|
28
|
+
].all?(&:right?) && generated_node.from_right.eql?(original_node.from_right)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return error report
|
32
|
+
#
|
33
|
+
# @return [String]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
def report
|
38
|
+
message = [identification]
|
39
|
+
|
40
|
+
message.concat(make_report('Original-Source', :original_source))
|
41
|
+
message.concat(make_report('Generated-Source', :generated_source))
|
42
|
+
message.concat(make_report('Original-Node', :original_node))
|
43
|
+
message.concat(make_report('Generated-Node', :generated_node))
|
44
|
+
message.concat(node_diff_report)
|
45
|
+
|
46
|
+
message.join("\n")
|
47
|
+
end
|
48
|
+
memoize :report
|
49
|
+
|
50
|
+
# Create validator from string
|
51
|
+
#
|
52
|
+
# @param [String] original_source
|
53
|
+
#
|
54
|
+
# @return [Validator]
|
55
|
+
def self.from_string(original_source)
|
56
|
+
original_node = Unparser
|
57
|
+
.parse_either(original_source)
|
58
|
+
.fmap(&Preprocessor.method(:run))
|
59
|
+
|
60
|
+
generated_source = original_node
|
61
|
+
.lmap(&method(:const_unit))
|
62
|
+
.bind(&method(:unparse_either))
|
63
|
+
|
64
|
+
generated_node = generated_source
|
65
|
+
.lmap(&method(:const_unit))
|
66
|
+
.bind(&Unparser.method(:parse_either))
|
67
|
+
.fmap(&Preprocessor.method(:run))
|
68
|
+
|
69
|
+
new(
|
70
|
+
identification: '(string)',
|
71
|
+
original_source: MPrelude::Either::Right.new(original_source),
|
72
|
+
original_node: original_node,
|
73
|
+
generated_source: generated_source,
|
74
|
+
generated_node: generated_node
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Create validator from file
|
79
|
+
#
|
80
|
+
# @param [Pathname] path
|
81
|
+
#
|
82
|
+
# @return [Validator]
|
83
|
+
def self.from_path(path)
|
84
|
+
from_string(path.read).with(identification: path.to_s)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# Create a labeled report from
|
90
|
+
#
|
91
|
+
# @param [String] label
|
92
|
+
# @param [Symbol] attribute_name
|
93
|
+
#
|
94
|
+
# @return [Array<String>]
|
95
|
+
def make_report(label, attribute_name)
|
96
|
+
["#{label}:"].concat(public_send(attribute_name).either(method(:report_exception), ->(value) { [value] }))
|
97
|
+
end
|
98
|
+
|
99
|
+
# Report optional exception
|
100
|
+
#
|
101
|
+
# @param [Exception, nil] exception
|
102
|
+
#
|
103
|
+
# @return [Array<String>]
|
104
|
+
def report_exception(exception)
|
105
|
+
if exception
|
106
|
+
[exception.inspect].concat(exception.backtrace.take(20))
|
107
|
+
else
|
108
|
+
['undefined']
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Report the node diff
|
113
|
+
#
|
114
|
+
# @return [Array<String>]
|
115
|
+
def node_diff_report
|
116
|
+
diff = nil
|
117
|
+
|
118
|
+
original_node.fmap do |original|
|
119
|
+
generated_node.fmap do |generated|
|
120
|
+
diff = Diff.new(
|
121
|
+
original.to_s.lines.map(&:chomp),
|
122
|
+
generated.to_s.lines.map(&:chomp)
|
123
|
+
).colorized_diff
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
diff ? ['Node-Diff:', diff] : []
|
128
|
+
end
|
129
|
+
|
130
|
+
# Create unit represented as nil
|
131
|
+
#
|
132
|
+
# @param [Object] _value
|
133
|
+
#
|
134
|
+
# @return [nil]
|
135
|
+
def self.const_unit(_value); end
|
136
|
+
private_class_method :const_unit
|
137
|
+
|
138
|
+
# Unparse capturing errors
|
139
|
+
#
|
140
|
+
# @param [Parser::AST::Node] node
|
141
|
+
#
|
142
|
+
# @return [Either<RuntimeError, String>]
|
143
|
+
def self.unparse_either(node)
|
144
|
+
MPrelude::Either
|
145
|
+
.wrap_error(RuntimeError) { Unparser.unparse(node) }
|
146
|
+
end
|
147
|
+
private_class_method :unparse_either
|
148
|
+
end # Validation
|
149
|
+
end # Unparser
|
@@ -81,26 +81,40 @@ describe 'Unparser on ruby corpus', mutant: false do
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
84
|
+
transform = Mutant::Transform
|
85
|
+
string = transform::Primitive.new(String)
|
86
|
+
string_array = transform::Array.new(string)
|
87
|
+
path = ROOT.join('spec', 'integrations.yml')
|
88
|
+
|
89
|
+
loader =
|
90
|
+
transform::Named.new(
|
91
|
+
path.to_s,
|
92
|
+
transform::Sequence.new(
|
93
|
+
[
|
94
|
+
transform::Exception.new(SystemCallError, :read.to_proc),
|
95
|
+
transform::Exception.new(YAML::SyntaxError, YAML.method(:safe_load)),
|
96
|
+
transform::Array.new(
|
97
|
+
transform::Sequence.new(
|
98
|
+
[
|
99
|
+
transform::Hash.new(
|
100
|
+
optional: [],
|
101
|
+
required: [
|
102
|
+
transform::Hash::Key.new('exclude', string_array),
|
103
|
+
transform::Hash::Key.new('name', string),
|
104
|
+
transform::Hash::Key.new('repo_ref', string),
|
105
|
+
transform::Hash::Key.new('repo_uri', string)
|
106
|
+
]
|
107
|
+
),
|
108
|
+
transform::Hash::Symbolize.new,
|
109
|
+
transform::Exception.new(Anima::Error, Project.public_method(:new))
|
110
|
+
]
|
111
|
+
)
|
112
|
+
)
|
113
|
+
]
|
114
|
+
)
|
115
|
+
)
|
102
116
|
|
103
|
-
ALL =
|
117
|
+
ALL = loader.apply(path).lmap(&:compact_message).from_right
|
104
118
|
end
|
105
119
|
|
106
120
|
Project::ALL.each do |project|
|
data/spec/integrations.yml
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
- name: rubyspec
|
19
19
|
repo_uri: 'https://github.com/ruby/spec.git'
|
20
20
|
# Revision of rubyspec on the last CI build of unparser that passed
|
21
|
-
repo_ref: '
|
21
|
+
repo_ref: 'b40189b88'
|
22
22
|
exclude:
|
23
23
|
- command_line/fixtures/bad_syntax.rb
|
24
24
|
- core/array/pack/shared/float.rb
|
@@ -40,10 +40,12 @@
|
|
40
40
|
- core/env/element_reference_spec.rb
|
41
41
|
- core/io/readpartial_spec.rb
|
42
42
|
- core/io/shared/gets_ascii.rb
|
43
|
+
- core/kernel/shared/sprintf_encoding.rb
|
43
44
|
- core/marshal/dump_spec.rb
|
44
45
|
- core/marshal/fixtures/marshal_data.rb
|
45
46
|
- core/marshal/shared/load.rb
|
46
47
|
- core/random/bytes_spec.rb
|
48
|
+
- core/regexp/shared/new.rb
|
47
49
|
- core/regexp/shared/new_ascii.rb
|
48
50
|
- core/regexp/shared/new_ascii_8bit.rb
|
49
51
|
- core/regexp/shared/quote.rb
|
@@ -51,6 +53,8 @@
|
|
51
53
|
- core/string/casecmp_spec.rb
|
52
54
|
- core/string/codepoints_spec.rb
|
53
55
|
- core/string/count_spec.rb
|
56
|
+
- core/string/encode_spec.rb
|
57
|
+
- core/string/inspect_spec.rb
|
54
58
|
- core/string/shared/codepoints.rb
|
55
59
|
- core/string/shared/each_codepoint_without_block.rb
|
56
60
|
- core/string/shared/eql.rb
|
@@ -71,6 +75,7 @@
|
|
71
75
|
- language/regexp/escapes_spec.rb
|
72
76
|
- language/source_encoding_spec.rb
|
73
77
|
- language/string_spec.rb
|
78
|
+
- library/base64/decode64_spec.rb
|
74
79
|
- library/digest/md5/shared/constants.rb
|
75
80
|
- library/digest/md5/shared/sample.rb
|
76
81
|
- library/digest/sha1/shared/constants.rb
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,34 @@
|
|
1
|
-
require '
|
1
|
+
require 'anima'
|
2
|
+
require 'mutant'
|
2
3
|
require 'pathname'
|
4
|
+
require 'timeout'
|
3
5
|
require 'unparser'
|
4
|
-
require '
|
5
|
-
require 'morpher'
|
6
|
-
require 'devtools/spec_helper'
|
6
|
+
require 'yaml'
|
7
7
|
|
8
8
|
require 'parser/current'
|
9
9
|
|
10
|
+
RSpec.configuration.around(file_path: %r{spec/unit}) do |example|
|
11
|
+
Timeout.timeout(0.1, &example)
|
12
|
+
end
|
13
|
+
|
14
|
+
RSpec.shared_examples_for 'a command method' do
|
15
|
+
it 'returns self' do
|
16
|
+
should equal(object)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
RSpec.shared_examples_for 'an idempotent method' do
|
21
|
+
it 'is idempotent' do
|
22
|
+
first = subject
|
23
|
+
fail 'RSpec not configured for threadsafety' unless RSpec.configuration.threadsafe?
|
24
|
+
mutex = __memoized.instance_variable_get(:@mutex)
|
25
|
+
memoized = __memoized.instance_variable_get(:@memoized)
|
26
|
+
|
27
|
+
mutex.synchronize { memoized.delete(:subject) }
|
28
|
+
should equal(first)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
10
32
|
module SpecHelper
|
11
33
|
def s(type, *children)
|
12
34
|
Parser::AST::Node.new(type, children)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Unparser::Color do
|
4
|
+
shared_examples 'actual color' do |code|
|
5
|
+
describe '#format' do
|
6
|
+
|
7
|
+
it 'returns formatted string' do
|
8
|
+
expect(apply).to eql("\e[#{code}mexample-string\e[0m")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#format' do
|
14
|
+
let(:input) { 'example-string' }
|
15
|
+
|
16
|
+
def apply
|
17
|
+
object.format(input)
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'RED' do
|
21
|
+
let(:object) { described_class::RED }
|
22
|
+
|
23
|
+
include_examples 'actual color', 31
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'GREEN' do
|
27
|
+
let(:object) { described_class::GREEN }
|
28
|
+
|
29
|
+
include_examples 'actual color', 32
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'NONE' do
|
33
|
+
let(:object) { described_class::NONE }
|
34
|
+
|
35
|
+
it 'returns original input' do
|
36
|
+
expect(apply).to be(input)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|