unparser 0.4.7 → 0.6.7
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 +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
|