unparser 0.4.7 → 0.6.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -9
- data/bin/unparser +2 -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/local_variable_scope.rb +6 -76
- data/lib/unparser/ast.rb +1 -3
- data/lib/unparser/buffer.rb +14 -25
- data/lib/unparser/cli.rb +85 -77
- data/lib/unparser/{cli/color.rb → color.rb} +4 -14
- data/lib/unparser/comments.rb +0 -26
- data/lib/unparser/concord.rb +114 -0
- data/lib/unparser/constants.rb +4 -53
- data/lib/unparser/diff.rb +98 -0
- data/lib/unparser/dsl.rb +0 -32
- data/lib/unparser/either.rb +153 -0
- data/lib/unparser/emitter/alias.rb +2 -8
- data/lib/unparser/emitter/args.rb +45 -0
- data/lib/unparser/emitter/argument.rb +13 -169
- data/lib/unparser/emitter/array.rb +27 -0
- data/lib/unparser/emitter/array_pattern.rb +29 -0
- data/lib/unparser/emitter/assignment.rb +36 -127
- data/lib/unparser/emitter/begin.rb +9 -84
- data/lib/unparser/emitter/binary.rb +7 -20
- data/lib/unparser/emitter/block.rb +57 -41
- data/lib/unparser/emitter/case.rb +6 -48
- data/lib/unparser/emitter/case_guard.rb +27 -0
- data/lib/unparser/emitter/case_match.rb +40 -0
- data/lib/unparser/emitter/cbase.rb +1 -3
- data/lib/unparser/emitter/class.rb +6 -26
- data/lib/unparser/emitter/const_pattern.rb +24 -0
- data/lib/unparser/emitter/def.rb +7 -51
- data/lib/unparser/emitter/defined.rb +2 -12
- data/lib/unparser/emitter/dstr.rb +22 -0
- data/lib/unparser/emitter/dsym.rb +41 -0
- data/lib/unparser/emitter/find_pattern.rb +18 -0
- data/lib/unparser/emitter/flipflop.rb +11 -10
- data/lib/unparser/emitter/float.rb +29 -0
- data/lib/unparser/emitter/flow_modifier.rb +15 -53
- data/lib/unparser/emitter/for.rb +5 -19
- data/lib/unparser/emitter/hash.rb +36 -0
- data/lib/unparser/emitter/hash_pattern.rb +67 -0
- data/lib/unparser/emitter/hookexe.rb +5 -11
- data/lib/unparser/emitter/if.rb +15 -71
- data/lib/unparser/emitter/in_match.rb +21 -0
- data/lib/unparser/emitter/in_pattern.rb +36 -0
- data/lib/unparser/emitter/index.rb +22 -89
- data/lib/unparser/emitter/kwargs.rb +13 -0
- data/lib/unparser/emitter/kwbegin.rb +31 -0
- data/lib/unparser/emitter/lambda.rb +0 -8
- data/lib/unparser/emitter/masgn.rb +20 -0
- data/lib/unparser/emitter/match.rb +3 -17
- data/lib/unparser/emitter/match_alt.rb +23 -0
- data/lib/unparser/emitter/match_as.rb +21 -0
- data/lib/unparser/emitter/match_pattern.rb +30 -0
- data/lib/unparser/emitter/match_pattern_p.rb +20 -0
- data/lib/unparser/emitter/match_rest.rb +33 -0
- data/lib/unparser/emitter/match_var.rb +19 -0
- data/lib/unparser/emitter/mlhs.rb +40 -0
- data/lib/unparser/emitter/module.rb +3 -9
- data/lib/unparser/emitter/op_assign.rb +14 -29
- data/lib/unparser/emitter/pair.rb +33 -0
- data/lib/unparser/emitter/pin.rb +19 -0
- data/lib/unparser/emitter/primitive.rb +93 -0
- data/lib/unparser/emitter/range.rb +35 -0
- data/lib/unparser/emitter/regexp.rb +35 -0
- data/lib/unparser/emitter/repetition.rb +17 -57
- data/lib/unparser/emitter/rescue.rb +1 -97
- data/lib/unparser/emitter/root.rb +17 -1
- data/lib/unparser/emitter/send.rb +10 -219
- data/lib/unparser/emitter/simple.rb +33 -0
- data/lib/unparser/emitter/splat.rb +13 -19
- data/lib/unparser/emitter/super.rb +1 -29
- data/lib/unparser/emitter/undef.rb +1 -9
- data/lib/unparser/emitter/variable.rb +1 -31
- data/lib/unparser/emitter/xstr.rb +72 -0
- data/lib/unparser/emitter/yield.rb +1 -9
- data/lib/unparser/emitter.rb +24 -425
- data/lib/unparser/equalizer.rb +98 -0
- data/lib/unparser/generation.rb +252 -0
- data/lib/unparser/node_details/send.rb +65 -0
- data/lib/unparser/node_details.rb +21 -0
- data/lib/unparser/node_helpers.rb +48 -6
- data/lib/unparser/validation.rb +172 -0
- data/lib/unparser/writer/binary.rb +99 -0
- data/lib/unparser/writer/dynamic_string.rb +211 -0
- data/lib/unparser/writer/resbody.rb +40 -0
- data/lib/unparser/writer/rescue.rb +43 -0
- data/lib/unparser/{emitter → writer}/send/attribute_assignment.rb +11 -26
- data/lib/unparser/writer/send/binary.rb +27 -0
- data/lib/unparser/writer/send/conditional.rb +25 -0
- data/lib/unparser/writer/send/regular.rb +33 -0
- data/lib/unparser/{emitter → writer}/send/unary.rb +10 -17
- data/lib/unparser/writer/send.rb +115 -0
- data/lib/unparser/writer.rb +15 -0
- data/lib/unparser.rb +161 -77
- metadata +100 -157
- data/.circleci/config.yml +0 -49
- data/.gitignore +0 -37
- data/.rspec +0 -4
- data/.rubocop.yml +0 -9
- data/Changelog.md +0 -156
- data/Gemfile +0 -9
- data/Gemfile.lock +0 -181
- data/LICENSE +0 -20
- data/Rakefile +0 -22
- data/config/devtools.yml +0 -2
- data/config/flay.yml +0 -3
- data/config/flog.yml +0 -2
- data/config/mutant.yml +0 -6
- data/config/reek.yml +0 -98
- data/config/rubocop.yml +0 -122
- data/config/yardstick.yml +0 -2
- data/lib/unparser/cli/differ.rb +0 -152
- data/lib/unparser/cli/source.rb +0 -267
- data/lib/unparser/emitter/empty.rb +0 -23
- data/lib/unparser/emitter/ensure.rb +0 -37
- data/lib/unparser/emitter/literal/array.rb +0 -29
- data/lib/unparser/emitter/literal/dynamic.rb +0 -53
- data/lib/unparser/emitter/literal/dynamic_body.rb +0 -132
- data/lib/unparser/emitter/literal/execute_string.rb +0 -38
- data/lib/unparser/emitter/literal/hash.rb +0 -156
- data/lib/unparser/emitter/literal/primitive.rb +0 -145
- data/lib/unparser/emitter/literal/range.rb +0 -36
- data/lib/unparser/emitter/literal/regexp.rb +0 -114
- data/lib/unparser/emitter/literal/singleton.rb +0 -26
- data/lib/unparser/emitter/literal.rb +0 -10
- data/lib/unparser/emitter/meta.rb +0 -16
- data/lib/unparser/emitter/redo.rb +0 -25
- data/lib/unparser/emitter/resbody.rb +0 -76
- data/lib/unparser/emitter/retry.rb +0 -25
- data/lib/unparser/emitter/send/binary.rb +0 -57
- data/lib/unparser/emitter/send/conditional.rb +0 -40
- data/lib/unparser/emitter/send/regular.rb +0 -40
- data/lib/unparser/preprocessor.rb +0 -159
- data/spec/integration/unparser/corpus_spec.rb +0 -111
- data/spec/integrations.yml +0 -92
- data/spec/spec_helper.rb +0 -20
- data/spec/unit/unparser/buffer/append_spec.rb +0 -24
- data/spec/unit/unparser/buffer/append_without_prefix_spec.rb +0 -23
- data/spec/unit/unparser/buffer/capture_content_spec.rb +0 -17
- data/spec/unit/unparser/buffer/content_spec.rb +0 -38
- data/spec/unit/unparser/buffer/fresh_line_spec.rb +0 -20
- data/spec/unit/unparser/buffer/indent_spec.rb +0 -20
- data/spec/unit/unparser/buffer/nl_spec.rb +0 -16
- data/spec/unit/unparser/buffer/unindent_spec.rb +0 -20
- data/spec/unit/unparser/comments/consume_spec.rb +0 -22
- data/spec/unit/unparser/comments/take_all_spec.rb +0 -19
- data/spec/unit/unparser/comments/take_before_spec.rb +0 -46
- data/spec/unit/unparser/comments/take_eol_comments_spec.rb +0 -32
- data/spec/unit/unparser/emitter/class_methods/handle_spec.rb +0 -17
- data/spec/unit/unparser_spec.rb +0 -1849
- data/unparser.gemspec +0 -32
data/lib/unparser/comments.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
module Unparser
|
4
4
|
|
5
5
|
# Holds the comments that remain to be emitted
|
6
|
-
#
|
7
|
-
# ignore :reek:RepeatedConditional
|
8
6
|
class Comments
|
9
7
|
|
10
8
|
# Proxy to singleton
|
@@ -113,39 +111,15 @@ module Unparser
|
|
113
111
|
|
114
112
|
private
|
115
113
|
|
116
|
-
# Take comments while the provided block returns true
|
117
|
-
#
|
118
|
-
# @yield [Parser::Source::Comment]
|
119
|
-
#
|
120
|
-
# @return [Array]
|
121
|
-
#
|
122
|
-
# @api private
|
123
|
-
#
|
124
114
|
def take_while
|
125
115
|
number_to_take = @comments.index { |comment| !yield(comment) } || @comments.size
|
126
116
|
@comments.shift(number_to_take)
|
127
117
|
end
|
128
118
|
|
129
|
-
# Take comments up to the line number
|
130
|
-
#
|
131
|
-
# @param [Fixnum] line
|
132
|
-
#
|
133
|
-
# @return [Array]
|
134
|
-
#
|
135
|
-
# @api private
|
136
|
-
#
|
137
119
|
def take_up_to_line(line)
|
138
120
|
take_while { |comment| comment.location.expression.line <= line }
|
139
121
|
end
|
140
122
|
|
141
|
-
# Unshift document comments and return the rest
|
142
|
-
#
|
143
|
-
# @param [Array] comments
|
144
|
-
#
|
145
|
-
# @return [Array]
|
146
|
-
#
|
147
|
-
# @api private
|
148
|
-
#
|
149
123
|
def unshift_documents(comments)
|
150
124
|
doc_comments, other_comments = comments.partition(&:document?)
|
151
125
|
doc_comments.reverse_each { |comment| @comments.unshift(comment) }
|
@@ -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/constants.rb
CHANGED
@@ -2,66 +2,19 @@
|
|
2
2
|
|
3
3
|
module Unparser
|
4
4
|
# All unparser constants maybe included in other libraries.
|
5
|
-
#
|
6
|
-
# False positive since constants are frozen dynamically
|
7
|
-
# to avoid duplication of `.freeze` calls
|
8
|
-
#
|
9
|
-
# :reek:TooManyConstants
|
10
5
|
module Constants
|
11
6
|
|
12
|
-
# Return frozen symbol set from enumerable
|
13
|
-
#
|
14
|
-
# @param [Enumerable] enumerable
|
15
|
-
#
|
16
|
-
# @return [Set<Symbol>]
|
17
|
-
#
|
18
|
-
# @api private
|
19
|
-
#
|
20
|
-
def self.symbol_set(enumerable)
|
21
|
-
enumerable.map(&:to_sym).freeze
|
22
|
-
end
|
23
|
-
private_class_method :symbol_set
|
24
|
-
|
25
|
-
BRACKETS_CURLY = IceNine.deep_freeze(%w[{ }])
|
26
|
-
BRACKETS_ROUND = IceNine.deep_freeze(%w[( )])
|
27
|
-
BRACKETS_SQUARE = IceNine.deep_freeze(%w([ ]))
|
28
|
-
|
29
7
|
# All unary operators of the ruby language
|
30
|
-
UNARY_OPERATORS =
|
8
|
+
UNARY_OPERATORS = %i[
|
31
9
|
! ~ -@ +@
|
32
|
-
]
|
10
|
+
].to_set.freeze
|
33
11
|
|
34
12
|
# All binary operators of the ruby language
|
35
|
-
BINARY_OPERATORS =
|
13
|
+
BINARY_OPERATORS = %i[
|
36
14
|
+ - * / & | && || << >> ==
|
37
15
|
=== != <= < <=> > >= =~ !~ ^
|
38
16
|
** %
|
39
|
-
]
|
40
|
-
|
41
|
-
COMMENT = '#'
|
42
|
-
|
43
|
-
WS = ' '
|
44
|
-
NL = "\n"
|
45
|
-
T_DOT = '.'
|
46
|
-
T_LT = '<'
|
47
|
-
T_DLT = '<<'
|
48
|
-
T_AMP = '&'
|
49
|
-
T_ASN = '='
|
50
|
-
T_SPLAT = '*'
|
51
|
-
T_DSPLAT = '**'
|
52
|
-
T_ASR = '=>'
|
53
|
-
T_PIPE = '|'
|
54
|
-
T_DCL = '::'
|
55
|
-
T_NEG = '!'
|
56
|
-
T_OR = '||'
|
57
|
-
T_AND = '&&'
|
58
|
-
T_COLON = ':'
|
59
|
-
|
60
|
-
M_PO = '('
|
61
|
-
M_PC = ')'
|
62
|
-
|
63
|
-
SNGL_QUOTE = "'"
|
64
|
-
DBL_QUOTE = '"'
|
17
|
+
].to_set.freeze
|
65
18
|
|
66
19
|
# Keywords
|
67
20
|
K_DO = 'do'
|
@@ -107,8 +60,6 @@ module Unparser
|
|
107
60
|
K_FILE = '__FILE__'
|
108
61
|
K_THEN = 'then'
|
109
62
|
|
110
|
-
DEFAULT_DELIMITER = ', '.freeze
|
111
|
-
|
112
63
|
KEYWORDS = constants.each_with_object([]) do |name, keywords|
|
113
64
|
value = const_get(name).freeze
|
114
65
|
next unless name.to_s.start_with?('K_')
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
# Class to create diffs from source code
|
5
|
+
class Diff
|
6
|
+
include Adamantium, 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
|
+
def diffs
|
63
|
+
::Diff::LCS.diff(old, new)
|
64
|
+
end
|
65
|
+
|
66
|
+
def hunks
|
67
|
+
diffs.map do |diff|
|
68
|
+
::Diff::LCS::Hunk.new(old.map(&:dup), new, diff, max_length, 0)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def minimized_hunk
|
73
|
+
head, *tail = hunks
|
74
|
+
|
75
|
+
tail.reduce(head) do |left, right|
|
76
|
+
right.merge(left)
|
77
|
+
right
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def max_length
|
82
|
+
[old, new].map(&:length).max
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.colorize_line(line)
|
86
|
+
case line[0]
|
87
|
+
when ADDITION
|
88
|
+
Color::GREEN
|
89
|
+
when DELETION
|
90
|
+
Color::RED
|
91
|
+
else
|
92
|
+
Color::NONE
|
93
|
+
end.format(line)
|
94
|
+
end
|
95
|
+
private_class_method :colorize_line
|
96
|
+
|
97
|
+
end # Diff
|
98
|
+
end # Unparser
|
data/lib/unparser/dsl.rb
CHANGED
@@ -6,14 +6,6 @@ module Unparser
|
|
6
6
|
|
7
7
|
private
|
8
8
|
|
9
|
-
# Define remaining children
|
10
|
-
#
|
11
|
-
# @param [Enumerable<Symbol>] names
|
12
|
-
#
|
13
|
-
# @return [undefined]
|
14
|
-
#
|
15
|
-
# @api private
|
16
|
-
#
|
17
9
|
def define_remaining_children(names)
|
18
10
|
range = names.length..-1
|
19
11
|
define_method(:remaining_children) do
|
@@ -22,15 +14,6 @@ module Unparser
|
|
22
14
|
private :remaining_children
|
23
15
|
end
|
24
16
|
|
25
|
-
# Define named child
|
26
|
-
#
|
27
|
-
# @param [Symbol] name
|
28
|
-
# @param [Fixnum] index
|
29
|
-
#
|
30
|
-
# @return [undefined]
|
31
|
-
#
|
32
|
-
# @api private
|
33
|
-
#
|
34
17
|
def define_child(name, index)
|
35
18
|
define_method(name) do
|
36
19
|
children.at(index)
|
@@ -38,15 +21,6 @@ module Unparser
|
|
38
21
|
private name
|
39
22
|
end
|
40
23
|
|
41
|
-
# Define a group of children
|
42
|
-
#
|
43
|
-
# @param [Symbol] name
|
44
|
-
# @param [Range] range
|
45
|
-
#
|
46
|
-
# @return [undefined]
|
47
|
-
#
|
48
|
-
# @api private
|
49
|
-
#
|
50
24
|
def define_group(name, range)
|
51
25
|
define_method(name) do
|
52
26
|
children[range]
|
@@ -55,12 +29,6 @@ module Unparser
|
|
55
29
|
memoize(name)
|
56
30
|
end
|
57
31
|
|
58
|
-
# Create name helpers
|
59
|
-
#
|
60
|
-
# @return [undefined]
|
61
|
-
#
|
62
|
-
# @api private
|
63
|
-
#
|
64
32
|
def children(*names)
|
65
33
|
define_remaining_children(names)
|
66
34
|
|
@@ -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
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
class Emitter
|
5
|
+
# Arguments emitter
|
6
|
+
class Args < self
|
7
|
+
def emit_block_arguments
|
8
|
+
delimited(normal_arguments)
|
9
|
+
|
10
|
+
write(',') if normal_arguments.one? && n_arg?(normal_arguments.first)
|
11
|
+
|
12
|
+
emit_shadowargs
|
13
|
+
end
|
14
|
+
|
15
|
+
def emit_def_arguments
|
16
|
+
delimited(normal_arguments)
|
17
|
+
end
|
18
|
+
|
19
|
+
def emit_lambda_arguments
|
20
|
+
delimited(normal_arguments)
|
21
|
+
emit_shadowargs
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def emit_shadowargs
|
27
|
+
return if shadowargs.empty?
|
28
|
+
|
29
|
+
write('; ')
|
30
|
+
delimited(shadowargs)
|
31
|
+
end
|
32
|
+
|
33
|
+
def normal_arguments
|
34
|
+
children.reject(&method(:n_shadowarg?))
|
35
|
+
end
|
36
|
+
memoize :normal_arguments
|
37
|
+
|
38
|
+
def shadowargs
|
39
|
+
children.select(&method(:n_shadowarg?))
|
40
|
+
end
|
41
|
+
memoize :shadowargs
|
42
|
+
|
43
|
+
end # Arguments
|
44
|
+
end # Emitter
|
45
|
+
end # Unparser
|