unparser 0.5.5 → 0.6.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.
- checksums.yaml +4 -4
- data/README.md +14 -2
- data/lib/unparser/abstract_type.rb +121 -0
- data/lib/unparser/adamantium/method_builder.rb +111 -0
- data/lib/unparser/adamantium.rb +150 -0
- data/lib/unparser/anima/attribute.rb +59 -0
- data/lib/unparser/anima/error.rb +23 -0
- data/lib/unparser/anima.rb +184 -0
- data/lib/unparser/ast.rb +1 -2
- data/lib/unparser/buffer.rb +2 -2
- data/lib/unparser/cli.rb +1 -0
- data/lib/unparser/color.rb +1 -1
- data/lib/unparser/concord.rb +114 -0
- data/lib/unparser/diff.rb +1 -1
- data/lib/unparser/either.rb +153 -0
- data/lib/unparser/emitter/flow_modifier.rb +6 -0
- data/lib/unparser/emitter/hash.rb +1 -31
- data/lib/unparser/emitter/hash_pattern.rb +1 -1
- data/lib/unparser/emitter/index.rb +1 -1
- data/lib/unparser/emitter/kwargs.rb +13 -0
- data/lib/unparser/emitter/match_pattern.rb +22 -0
- data/lib/unparser/emitter/op_assign.rb +2 -2
- data/lib/unparser/emitter/pair.rb +33 -0
- data/lib/unparser/emitter/primitive.rb +2 -2
- data/lib/unparser/emitter/simple.rb +2 -2
- data/lib/unparser/emitter.rb +1 -1
- data/lib/unparser/equalizer.rb +98 -0
- data/lib/unparser/node_details/send.rb +5 -2
- data/lib/unparser/node_details.rb +1 -1
- data/lib/unparser/node_helpers.rb +3 -1
- data/lib/unparser/validation.rb +3 -3
- data/lib/unparser/writer/binary.rb +1 -1
- data/lib/unparser/writer/dynamic_string.rb +12 -22
- data/lib/unparser/writer/rescue.rb +1 -1
- data/lib/unparser/writer/send/unary.rb +1 -1
- data/lib/unparser/writer/send.rb +9 -18
- data/lib/unparser.rb +34 -24
- metadata +30 -113
data/lib/unparser/buffer.rb
CHANGED
data/lib/unparser/cli.rb
CHANGED
data/lib/unparser/color.rb
CHANGED
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
# A mixin to define a composition
|
5
|
+
#
|
6
|
+
# Original code before vendoring and reduction from: https://github.com/mbj/concord.
|
7
|
+
class Concord < Module
|
8
|
+
include Adamantium, Equalizer.new(:names)
|
9
|
+
|
10
|
+
# The maximum number of objects the hosting class is composed of
|
11
|
+
MAX_NR_OF_OBJECTS = 3
|
12
|
+
|
13
|
+
# Return names
|
14
|
+
#
|
15
|
+
# @return [Enumerable<Symbol>]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
attr_reader :names
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Initialize object
|
24
|
+
#
|
25
|
+
# @return [undefined]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
#
|
29
|
+
# rubocop:disable Lint/MissingSuper
|
30
|
+
def initialize(*names)
|
31
|
+
if names.length > MAX_NR_OF_OBJECTS
|
32
|
+
fail "Composition of more than #{MAX_NR_OF_OBJECTS} objects is not allowed"
|
33
|
+
end
|
34
|
+
|
35
|
+
@names = names
|
36
|
+
define_initialize
|
37
|
+
define_readers
|
38
|
+
define_equalizer
|
39
|
+
end
|
40
|
+
# rubocop:enable Lint/MissingSuper
|
41
|
+
|
42
|
+
# Define equalizer
|
43
|
+
#
|
44
|
+
# @return [undefined]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
#
|
48
|
+
def define_equalizer
|
49
|
+
include(Equalizer.new(*names))
|
50
|
+
end
|
51
|
+
|
52
|
+
# Define readers
|
53
|
+
#
|
54
|
+
# @return [undefined]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
def define_readers
|
59
|
+
attribute_names = names
|
60
|
+
attr_reader(*attribute_names)
|
61
|
+
|
62
|
+
protected(*attribute_names) if attribute_names.any?
|
63
|
+
end
|
64
|
+
|
65
|
+
# Define initialize method
|
66
|
+
#
|
67
|
+
# @return [undefined]
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
#
|
71
|
+
#
|
72
|
+
def define_initialize
|
73
|
+
ivars = instance_variable_names
|
74
|
+
size = names.size
|
75
|
+
|
76
|
+
define_method :initialize do |*args|
|
77
|
+
args_size = args.size
|
78
|
+
unless args_size.equal?(size)
|
79
|
+
fail ArgumentError, "wrong number of arguments (#{args_size} for #{size})"
|
80
|
+
end
|
81
|
+
|
82
|
+
ivars.zip(args) { |ivar, arg| instance_variable_set(ivar, arg) }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return instance variable names
|
87
|
+
#
|
88
|
+
# @return [String]
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
#
|
92
|
+
def instance_variable_names
|
93
|
+
names.map { |name| "@#{name}" }
|
94
|
+
end
|
95
|
+
|
96
|
+
# Mixin for public attribute readers
|
97
|
+
class Public < self
|
98
|
+
|
99
|
+
# Hook called when module is included
|
100
|
+
#
|
101
|
+
# @param [Class,Module] descendant
|
102
|
+
#
|
103
|
+
# @return [undefined]
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
#
|
107
|
+
def included(descendant)
|
108
|
+
names.each do |name|
|
109
|
+
descendant.__send__(:public, name)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end # Public
|
113
|
+
end # Concord
|
114
|
+
end # Unparser
|
data/lib/unparser/diff.rb
CHANGED
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
module RequireBlock
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
# Raise error unless block is provided
|
9
|
+
#
|
10
|
+
# @raise [MissingBlockError]
|
11
|
+
# if no block is given
|
12
|
+
#
|
13
|
+
# @return [self]
|
14
|
+
def require_block
|
15
|
+
fail LocalJumpError unless block_given?
|
16
|
+
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end # RequireBLock
|
20
|
+
|
21
|
+
class Either
|
22
|
+
include(
|
23
|
+
Adamantium,
|
24
|
+
Concord.new(:value),
|
25
|
+
RequireBlock
|
26
|
+
)
|
27
|
+
|
28
|
+
# Execute block and wrap error in left
|
29
|
+
#
|
30
|
+
# @param [Class<Exception>] exception
|
31
|
+
#
|
32
|
+
# @return [Either<Exception, Object>]
|
33
|
+
def self.wrap_error(*exceptions)
|
34
|
+
Right.new(yield)
|
35
|
+
rescue *exceptions => error
|
36
|
+
Left.new(error)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Test for left constructor
|
40
|
+
#
|
41
|
+
# @return [Boolean]
|
42
|
+
def left?
|
43
|
+
instance_of?(Left)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Test for right constructor
|
47
|
+
#
|
48
|
+
# @return [Boolean]
|
49
|
+
def right?
|
50
|
+
instance_of?(Right)
|
51
|
+
end
|
52
|
+
|
53
|
+
class Left < self
|
54
|
+
# Evaluate functor block
|
55
|
+
#
|
56
|
+
# @return [Either::Left<Object>]
|
57
|
+
def fmap(&block)
|
58
|
+
require_block(&block)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Evaluate applicative block
|
62
|
+
#
|
63
|
+
# @return [Either::Left<Object>]
|
64
|
+
def bind(&block)
|
65
|
+
require_block(&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Unwrap value from left
|
69
|
+
#
|
70
|
+
# @return [Object]
|
71
|
+
def from_left
|
72
|
+
value
|
73
|
+
end
|
74
|
+
|
75
|
+
# Unwrap value from right
|
76
|
+
#
|
77
|
+
# @return [Object]
|
78
|
+
#
|
79
|
+
def from_right
|
80
|
+
if block_given?
|
81
|
+
yield(value)
|
82
|
+
else
|
83
|
+
fail "Expected right value, got #{inspect}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Map over left value
|
88
|
+
#
|
89
|
+
# @return [Either::Right<Object>]
|
90
|
+
def lmap
|
91
|
+
Left.new(yield(value))
|
92
|
+
end
|
93
|
+
|
94
|
+
# Evaluate left side of branch
|
95
|
+
#
|
96
|
+
# @param [#call] left
|
97
|
+
# @param [#call] _right
|
98
|
+
def either(left, _right)
|
99
|
+
left.call(value)
|
100
|
+
end
|
101
|
+
end # Left
|
102
|
+
|
103
|
+
class Right < self
|
104
|
+
# Evaluate functor block
|
105
|
+
#
|
106
|
+
# @return [Either::Right<Object>]
|
107
|
+
def fmap
|
108
|
+
Right.new(yield(value))
|
109
|
+
end
|
110
|
+
|
111
|
+
# Evaluate applicative block
|
112
|
+
#
|
113
|
+
# @return [Either<Object>]
|
114
|
+
def bind
|
115
|
+
yield(value)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Unwrap value from left
|
119
|
+
#
|
120
|
+
# @return [Object]
|
121
|
+
#
|
122
|
+
def from_left
|
123
|
+
if block_given?
|
124
|
+
yield(value)
|
125
|
+
else
|
126
|
+
fail "Expected left value, got #{inspect}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Unwrap value from right
|
131
|
+
#
|
132
|
+
# @return [Object]
|
133
|
+
def from_right
|
134
|
+
value
|
135
|
+
end
|
136
|
+
|
137
|
+
# Map over left value
|
138
|
+
#
|
139
|
+
# @return [Either::Right<Object>]
|
140
|
+
def lmap(&block)
|
141
|
+
require_block(&block)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Evaluate right side of branch
|
145
|
+
#
|
146
|
+
# @param [#call] _left
|
147
|
+
# @param [#call] right
|
148
|
+
def either(_left, right)
|
149
|
+
right.call(value)
|
150
|
+
end
|
151
|
+
end # Right
|
152
|
+
end # Either
|
153
|
+
end # Unparser
|
@@ -4,10 +4,6 @@ module Unparser
|
|
4
4
|
class Emitter
|
5
5
|
# Emitter for Hash literals
|
6
6
|
class Hash < self
|
7
|
-
BAREWORD = /\A[A-Za-z_][A-Za-z_0-9]*[?!]?\z/.freeze
|
8
|
-
|
9
|
-
private_constant(*constants(false))
|
10
|
-
|
11
7
|
handle :hash
|
12
8
|
|
13
9
|
def emit_last_argument_hash
|
@@ -41,34 +37,8 @@ module Unparser
|
|
41
37
|
end
|
42
38
|
|
43
39
|
def emit_hash_body
|
44
|
-
delimited(children
|
45
|
-
end
|
46
|
-
|
47
|
-
def emit_hash_member(node)
|
48
|
-
if n_kwsplat?(node)
|
49
|
-
visit(node)
|
50
|
-
else
|
51
|
-
emit_pair(node)
|
52
|
-
end
|
40
|
+
delimited(children)
|
53
41
|
end
|
54
|
-
|
55
|
-
def emit_pair(pair)
|
56
|
-
key, value = *pair.children
|
57
|
-
|
58
|
-
if colon?(key)
|
59
|
-
write(key.children.first.to_s, ': ')
|
60
|
-
else
|
61
|
-
visit(key)
|
62
|
-
write(' => ')
|
63
|
-
end
|
64
|
-
|
65
|
-
visit(value)
|
66
|
-
end
|
67
|
-
|
68
|
-
def colon?(key)
|
69
|
-
n_sym?(key) && BAREWORD.match?(key.children.first)
|
70
|
-
end
|
71
|
-
|
72
42
|
end # Hash
|
73
43
|
end # Emitter
|
74
44
|
end # Unparser
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
class Emitter
|
5
|
+
# Emitter for in pattern nodes
|
6
|
+
class MatchPattern < self
|
7
|
+
|
8
|
+
handle :match_pattern
|
9
|
+
handle :match_pattern_p
|
10
|
+
|
11
|
+
children :target, :pattern
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def dispatch
|
16
|
+
visit(target)
|
17
|
+
write(' in ')
|
18
|
+
visit(pattern)
|
19
|
+
end
|
20
|
+
end # InPattern
|
21
|
+
end # Emitter
|
22
|
+
end # Unparser
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
class Emitter
|
5
|
+
# Emitter for key value pairs in hash literals or kwargs
|
6
|
+
class Pair < self
|
7
|
+
BAREWORD = /\A[A-Za-z_][A-Za-z_0-9]*[?!]?\z/.freeze
|
8
|
+
|
9
|
+
private_constant(*constants(false))
|
10
|
+
|
11
|
+
handle :pair
|
12
|
+
|
13
|
+
children :key, :value
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def dispatch
|
18
|
+
if colon?(key)
|
19
|
+
write(key.children.first.to_s, ': ')
|
20
|
+
else
|
21
|
+
visit(key)
|
22
|
+
write(' => ')
|
23
|
+
end
|
24
|
+
|
25
|
+
visit(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def colon?(key)
|
29
|
+
n_sym?(key) && BAREWORD.match?(key.children.first)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -4,7 +4,7 @@ module Unparser
|
|
4
4
|
class Emitter
|
5
5
|
# Emitter for simple nodes that generate a single token
|
6
6
|
class Simple < self
|
7
|
-
MAP =
|
7
|
+
MAP = {
|
8
8
|
__ENCODING__: '__ENCODING__',
|
9
9
|
__FILE__: '__FILE__',
|
10
10
|
__LINE__: '__LINE__',
|
@@ -19,7 +19,7 @@ module Unparser
|
|
19
19
|
self: 'self',
|
20
20
|
true: 'true',
|
21
21
|
zsuper: 'super'
|
22
|
-
|
22
|
+
}.freeze
|
23
23
|
|
24
24
|
handle(*MAP.keys)
|
25
25
|
|
data/lib/unparser/emitter.rb
CHANGED
@@ -5,7 +5,7 @@ module Unparser
|
|
5
5
|
|
6
6
|
# Emitter base class
|
7
7
|
class Emitter
|
8
|
-
include Adamantium
|
8
|
+
include Adamantium, AbstractType, Constants, Generation, NodeHelpers
|
9
9
|
include Anima.new(:buffer, :comments, :node, :local_variable_scope)
|
10
10
|
|
11
11
|
public :node
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
# Define equality, equivalence and inspection methods
|
5
|
+
#
|
6
|
+
# Original code before vendoring and reduction from: https://github.com/dkubb/equalizer.
|
7
|
+
class Equalizer < Module
|
8
|
+
# Initialize an Equalizer with the given keys
|
9
|
+
#
|
10
|
+
# Will use the keys with which it is initialized to define #cmp?,
|
11
|
+
# #hash, and #inspect
|
12
|
+
#
|
13
|
+
# @param [Array<Symbol>] keys
|
14
|
+
#
|
15
|
+
# @return [undefined]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
# rubocop:disable Lint/MissingSuper
|
20
|
+
def initialize(*keys)
|
21
|
+
@keys = keys
|
22
|
+
define_methods
|
23
|
+
freeze
|
24
|
+
end
|
25
|
+
# rubocop:enable Lint/MissingSuper
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def included(descendant)
|
30
|
+
descendant.include(Methods)
|
31
|
+
end
|
32
|
+
|
33
|
+
def define_methods
|
34
|
+
define_cmp_method
|
35
|
+
define_hash_method
|
36
|
+
define_inspect_method
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_cmp_method
|
40
|
+
keys = @keys
|
41
|
+
define_method(:cmp?) do |comparator, other|
|
42
|
+
keys.all? do |key|
|
43
|
+
__send__(key).public_send(comparator, other.__send__(key))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
private :cmp?
|
47
|
+
end
|
48
|
+
|
49
|
+
def define_hash_method
|
50
|
+
keys = @keys
|
51
|
+
define_method(:hash) do
|
52
|
+
keys.map(&public_method(:__send__)).push(self.class).hash
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def define_inspect_method
|
57
|
+
keys = @keys
|
58
|
+
define_method(:inspect) do
|
59
|
+
klass = self.class
|
60
|
+
name = klass.name || klass.inspect
|
61
|
+
"#<#{name}#{keys.map { |key| " #{key}=#{__send__(key).inspect}" }.join}>"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# The comparison methods
|
66
|
+
module Methods
|
67
|
+
# Compare the object with other object for equality
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# object.eql?(other) # => true or false
|
71
|
+
#
|
72
|
+
# @param [Object] other
|
73
|
+
# the other object to compare with
|
74
|
+
#
|
75
|
+
# @return [Boolean]
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
def eql?(other)
|
79
|
+
instance_of?(other.class) && cmp?(__method__, other)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Compare the object with other object for equivalency
|
83
|
+
#
|
84
|
+
# @example
|
85
|
+
# object == other # => true or false
|
86
|
+
#
|
87
|
+
# @param [Object] other
|
88
|
+
# the other object to compare with
|
89
|
+
#
|
90
|
+
# @return [Boolean]
|
91
|
+
#
|
92
|
+
# @api public
|
93
|
+
def ==(other)
|
94
|
+
instance_of?(other.class) && cmp?(__method__, other)
|
95
|
+
end
|
96
|
+
end # module Methods
|
97
|
+
end # class Equalizer
|
98
|
+
end # Unparser
|
@@ -19,7 +19,10 @@ module Unparser
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def binary_syntax_allowed?
|
22
|
-
selector_binary_operator?
|
22
|
+
selector_binary_operator? \
|
23
|
+
&& arguments.one? \
|
24
|
+
&& !n_splat?(arguments.first) \
|
25
|
+
&& !n_kwargs?(arguments.first)
|
23
26
|
end
|
24
27
|
|
25
28
|
def selector_unary_operator?
|
@@ -48,7 +51,7 @@ module Unparser
|
|
48
51
|
memoize :assignment?
|
49
52
|
|
50
53
|
def arguments
|
51
|
-
children[2
|
54
|
+
children[2..]
|
52
55
|
end
|
53
56
|
memoize :arguments
|
54
57
|
|
@@ -36,18 +36,20 @@ module Unparser
|
|
36
36
|
args
|
37
37
|
array
|
38
38
|
array_pattern
|
39
|
-
empty_else
|
40
39
|
begin
|
41
40
|
block
|
42
41
|
cbase
|
43
42
|
const
|
44
43
|
dstr
|
44
|
+
empty_else
|
45
45
|
ensure
|
46
46
|
hash
|
47
47
|
hash_pattern
|
48
48
|
if
|
49
49
|
in_pattern
|
50
50
|
int
|
51
|
+
kwarg
|
52
|
+
kwargs
|
51
53
|
kwsplat
|
52
54
|
lambda
|
53
55
|
match_rest
|
data/lib/unparser/validation.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Unparser
|
4
4
|
# Validation of unparser results
|
5
5
|
class Validation
|
6
|
-
include Adamantium
|
6
|
+
include Adamantium, Anima.new(
|
7
7
|
:generated_node,
|
8
8
|
:generated_source,
|
9
9
|
:identification,
|
@@ -64,7 +64,7 @@ module Unparser
|
|
64
64
|
|
65
65
|
new(
|
66
66
|
identification: '(string)',
|
67
|
-
original_source:
|
67
|
+
original_source: Either::Right.new(original_source),
|
68
68
|
original_node: original_node,
|
69
69
|
generated_source: generated_source,
|
70
70
|
generated_node: generated_node
|
@@ -86,7 +86,7 @@ module Unparser
|
|
86
86
|
new(
|
87
87
|
identification: '(string)',
|
88
88
|
original_source: generated_source,
|
89
|
-
original_node:
|
89
|
+
original_node: Either::Right.new(original_node),
|
90
90
|
generated_source: generated_source,
|
91
91
|
generated_node: generated_node
|
92
92
|
)
|