ruby_list_comprehension 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ruby_list_comprehension.rb +210 -110
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa7eade3470c5cf0a30200ae86f39f05383804d6ddc0e35faa3fb18d01da7dcc
|
4
|
+
data.tar.gz: 1ce2185e0da4ee30e402ee844bd1a87d5beacb2faf263fdd13618ae6e7eba1d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a122824fd982fdfecf007a83860b8006a4cefbeed77013be38b10284d3b6c1e3e525d45c131cce0d6dc405af581a417639ee65141bbb85cf07d053c6a23520d5
|
7
|
+
data.tar.gz: e9b2476e5f6ac2cf86910218642a21c4c2c7cbd679d70654c4b0ad5f403cac0563b81df25704f8c6392db44e57e4a2d8f56207b62b3b4542ddb762db63201cc8
|
@@ -1,130 +1,230 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
require 'readline'
|
3
|
+
require 'singleton'
|
4
|
+
require 'set'
|
5
|
+
module RubyList
|
6
|
+
FM_REGEX = /for(?<parameter>.*)(?=in)in(?<iterable>.*)(?=do)do(?<mappable>.*)(?=if)if(?<filterable>.+)end/.freeze
|
7
|
+
F_REGEX = /for(?<parameter>.*)(?=in)in(?<iterable>.*)(.+)do(.+)(?=if)if(?<filterable>.*)end/.freeze
|
8
|
+
M_REGEX = /(?=for)for(?<parameter>.*)(?=in)in(?<iterable>.*)(?=do)do(?<mappable>.*)(?=end)end/.freeze
|
9
|
+
I_REGEX = /for(?<parameter>.*)(?=in)in(?<iterable>.*)(?=do)do(?<identity>.+)(?=end)end/.freeze
|
4
10
|
|
5
|
-
|
6
|
-
|
7
|
-
|
11
|
+
def fetch_capture(data, name)
|
12
|
+
data[name.to_sym] if data.names.include?(name.to_s)
|
13
|
+
end
|
8
14
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
+
def op_type(str)
|
16
|
+
match_map = str.match(M_REGEX)
|
17
|
+
match_filter_map = str.match(FM_REGEX)
|
18
|
+
match_filter = str.match(F_REGEX)
|
19
|
+
match_identity = str.match(I_REGEX)
|
20
|
+
parameter = fetch_capture(match_identity, :parameter)
|
21
|
+
mappable = fetch_capture(match_map, :mappable)
|
22
|
+
if match_filter_map.nil? && match_filter.nil?
|
23
|
+
if parameter.strip == mappable.strip || /\A\s*\Z/ === mappable
|
24
|
+
return :identity
|
25
|
+
else
|
26
|
+
return :map
|
27
|
+
end
|
28
|
+
end
|
29
|
+
map_condition = mappable.split('if')[0]
|
30
|
+
if fetch_capture(match_map, :mappable).include?('if') && !(parameter.strip == map_condition.strip || /\A\s*\Z/ === mappable)
|
31
|
+
return :filter_map
|
15
32
|
else
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@line = file_data[@location - 1].strip
|
21
|
-
start = @line.index('l[')
|
22
|
-
ending = @line[start..-1].index('end')
|
23
|
-
@line = @line[start+2...ending + 6].chop
|
33
|
+
if match_identity[:parameter].strip == match_filter[:filterable].strip
|
34
|
+
return :identity
|
35
|
+
end
|
36
|
+
return :filter
|
24
37
|
end
|
25
|
-
# p @line
|
26
|
-
c[@line]
|
27
38
|
end
|
28
39
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@list = list.strip
|
41
|
-
case list
|
42
|
-
when '{}' then return [{}]
|
43
|
-
when '[]' then return [[]]
|
44
|
-
when '' then return []
|
45
|
-
else list += ' end' unless list.include?('end')
|
46
|
-
end
|
47
|
-
raise 'syntax error in list comprehension' if list.length < 10
|
48
|
-
|
49
|
-
if @caching && @cache[list]
|
50
|
-
@cache_count += 1
|
51
|
-
return @cache[list]
|
52
|
-
end
|
40
|
+
def denest_builder(nested_list)
|
41
|
+
nested_list
|
42
|
+
nested_list
|
43
|
+
len = 0
|
44
|
+
nested_list
|
45
|
+
fin = nested_list.split('[').delete_if{|x| x == ""}
|
46
|
+
fin = fin.map{|str|str[0..-1] + " end"}
|
47
|
+
fin = fin.join.split('],')
|
48
|
+
fin = fin.join.split(' end')
|
49
|
+
fin.map{|x|"#{x} end"}
|
50
|
+
end
|
53
51
|
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
def denest_flattener(nested_list)
|
53
|
+
nested_list
|
54
|
+
len = nested_list.index('for')
|
55
|
+
nested_list_array = nested_list.split(" end").join
|
56
|
+
nested_list_array.split(' end')
|
57
|
+
nested_list_array = nested_list_array.split('for')[1..-1].map!{|x|"for#{x} end"}
|
58
|
+
end
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
raise 'incorrect syntax for list comprehension' + "\n" + se.to_s
|
60
|
+
def create_protocol(denested_array, operator_array)
|
61
|
+
operator_array.each_with_object({}).with_index do |(e, o), i|
|
62
|
+
case e
|
63
|
+
when :filter
|
64
|
+
o[i] = [e, denested_array[i].match(F_REGEX)]
|
65
|
+
when :filter_map
|
66
|
+
o[i] = [e, denested_array[i].match(FM_REGEX)]
|
67
|
+
when :map
|
68
|
+
o[i] = [e, denested_array[i].match(M_REGEX)]
|
69
|
+
when :identity
|
70
|
+
o[i] = [e, denested_array[i].match(I_REGEX)]
|
70
71
|
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def execute_comprehension(hash, flatten = true)
|
76
|
+
$process_str = ''
|
77
|
+
op_array = hash.values.map{|x,|x}
|
78
|
+
start_index = op_array.length - 1
|
79
|
+
nested = op_array.length > 1
|
80
|
+
match_data_array = hash.values.map{|_,x|x}
|
71
81
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
82
|
+
i = 0
|
83
|
+
until op_array.empty?
|
84
|
+
current_data = match_data_array[i]
|
85
|
+
current_op = op_array[0]
|
86
|
+
$iterable_string = fetch_capture(current_data, 'iterable')
|
87
|
+
$mappable = fetch_capture(current_data, 'mappable')
|
88
|
+
$parameter = fetch_capture(current_data, 'parameter')
|
89
|
+
$filterable = fetch_capture(current_data, 'filterable')
|
90
|
+
$identity = fetch_capture(current_data, 'identity')
|
91
|
+
$iterable = Array($iterable) if $iterable.class == Set
|
92
|
+
$iterable = fetch_capture(current_data, 'iterable') if i != 0
|
93
|
+
$iterable = $iterable_string if !flatten && @nested
|
94
|
+
case op_array.shift
|
95
|
+
when :identity
|
96
|
+
flatten = @flattener
|
97
|
+
if !@nested
|
98
|
+
return *$iterable
|
99
|
+
elsif i.zero? && !@flattener
|
100
|
+
$process_str += "(#$iterable).map"
|
101
|
+
elsif i.zero? && @flattener
|
102
|
+
$process_str += "(#$iterable).flat_map"
|
103
|
+
elsif !i.zero? && i != start_index
|
104
|
+
$process_str += "{(#$iterable).map"
|
105
|
+
elsif i == start_index
|
106
|
+
$process_str += "{(#$iterable_string).map(&:itself)"
|
107
|
+
$process_str += '}' * (start_index)
|
108
|
+
elsif nested && i.zero? && !flatten
|
109
|
+
$process_str += "(#$iterable).map"
|
110
|
+
elsif i != 0 && nested
|
111
|
+
$process_str += "(#$iterable_string).map"
|
81
112
|
end
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
@op[@count] = 'filter'
|
99
|
-
@count += 1
|
100
|
-
return @iterable.filter { |x| instance_eval(@filterable.gsub(@var, x.to_s)) }
|
113
|
+
when :map
|
114
|
+
if i == 0
|
115
|
+
$mappable = $parameter if /\A\s*\Z/ === $mappable
|
116
|
+
$process_str += "(#$iterable).map{|(#$parameter)|(#$mappable)}"
|
117
|
+
elsif i == 0 && flatten
|
118
|
+
$mappable = $parameter if /\A\s*\Z/ === $mappable
|
119
|
+
$process_str += "(#$iterable).flat_map{|(#$parameter)|(#$mappable)"
|
120
|
+
elsif i == 0 && !flatten
|
121
|
+
$mappable = $parameter if /\A\s*\Z/ === $mappable
|
122
|
+
$process_str += "(#$iterable).map{|(#$parameter)|(#$mappable)"
|
123
|
+
elsif i == start_index
|
124
|
+
$mappable = $parameter if /\A\s*\Z/ === $mappable
|
125
|
+
$process_str += "{(#$iterable).map{|(#$parameter)|(#$mappable)"
|
126
|
+
$process_str += '}' * (start_index+1)
|
127
|
+
elsif i != start_index
|
128
|
+
$process_str += "{(#$iterable_string).map{|(#$parameter)|(#$mappable)"
|
101
129
|
end
|
102
|
-
|
103
|
-
if
|
104
|
-
|
105
|
-
@count += 1
|
106
|
-
return @iterable.map { |x| instance_eval(@mappable.gsub(@var, x.to_s)) }
|
130
|
+
when :filter
|
131
|
+
if op_array.empty?
|
132
|
+
$process_str += "(#$iterable).filter{|(#$parameter)|(#$filterable)}"
|
107
133
|
end
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
if @version >= '2.7.0'
|
112
|
-
@op[@count] = 'filter_map'
|
113
|
-
@count += 1
|
114
|
-
return @iterable.filter_map { |x| instance_eval(filter_map_condition_args.gsub(@var, x.to_s)) }
|
134
|
+
when :filter_map
|
135
|
+
if op_array.empty? && RUBY_VERSION >= '2.7.0'
|
136
|
+
$process_str += "(#$iterable).filter_map{|(#$parameter)|(#$mappable) if (#$filterable)}"
|
115
137
|
else
|
116
|
-
|
117
|
-
@count += 1
|
118
|
-
@iterable.map { |x| instance_eval(filter_map_condition_args.gsub!(@var, x.to_s)) }.compact!
|
138
|
+
$process_str += "(#$iterable).map{|(#$parameter)|(#$mappable) if (#$filterable)}.compact"
|
119
139
|
end
|
120
140
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
141
|
+
i += 1
|
142
|
+
# p $process_str
|
143
|
+
end
|
144
|
+
begin
|
145
|
+
current_op
|
146
|
+
instance_eval($process_str)
|
147
|
+
rescue SyntaxError => e
|
148
|
+
'List imcomprehensible :' + e.backtrace_locations.to_s
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def one_shot(str)
|
153
|
+
len = str.scan('for ').length
|
154
|
+
@nested = (len > 1)
|
155
|
+
nested = @nested
|
156
|
+
if nested
|
157
|
+
nest_mode = str.include?('end end') && !nested ? :flatten : :matrix
|
158
|
+
nest_array = @flattener ? denest_flattener(str) : denest_builder(str)
|
159
|
+
else
|
160
|
+
nest_array = [str]
|
161
|
+
end
|
162
|
+
op_array = nest_array.map(&method(:op_type))
|
163
|
+
hash = create_protocol(nest_array, op_array)
|
164
|
+
begin
|
165
|
+
execute_comprehension(hash, nest_mode==nested)
|
166
|
+
rescue SyntaxError => se
|
167
|
+
puts 'RESCUED!' + se.backtrace_locations.to_s
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class ListComprehension
|
172
|
+
include Singleton
|
173
|
+
include RubyList
|
174
|
+
attr_accessor :cache, :caching, :mappable, :filterable, :iterable, :var, :list, :location, :line, :count
|
175
|
+
attr_accessor :flattener, :len, :nested, :nested_var, :file, :list_comp, :final_iterable_value
|
176
|
+
attr_accessor :count
|
177
|
+
REPL_LIST = %w[:irb :pry].freeze
|
178
|
+
|
179
|
+
def initialize
|
180
|
+
@cache = {}
|
181
|
+
@caching = true
|
182
|
+
@version = RUBY_VERSION
|
183
|
+
end
|
184
|
+
|
185
|
+
def [](*iterable)
|
186
|
+
return [] if iterable.empty? || iterable.nil?
|
187
|
+
|
188
|
+
|
189
|
+
@flattener = iterable.length == 1
|
190
|
+
iterable = iterable[0] if iterable.length == 1
|
191
|
+
@final_iterable_value = iterable
|
192
|
+
@list_comp = iterable
|
193
|
+
$iterable = iterable
|
194
|
+
@filename = $PROGRAM_NAME
|
195
|
+
@line = REPL_LIST.include?(@filename.to_sym) ? locate_list_repl : locate_list_file
|
196
|
+
return @cache[@line] if @cache.has_key?(@line)
|
197
|
+
|
198
|
+
@cache[@line] = one_shot(@line)
|
199
|
+
end
|
200
|
+
|
201
|
+
def locate_list_repl
|
202
|
+
@line = Readline::HISTORY.to_a.reverse.uniq.reverse[-1]
|
203
|
+
start = @line.index('l[') + 2
|
204
|
+
ending = @line[start..-1].index('end') + 5
|
205
|
+
@line = @line[start...ending]
|
206
|
+
end
|
207
|
+
|
208
|
+
def retrieve_file_data
|
209
|
+
@file = File.open($PROGRAM_NAME)
|
210
|
+
file_data = @file.readlines.map(&:chomp)
|
211
|
+
@file.close
|
212
|
+
file_data
|
213
|
+
end
|
214
|
+
|
215
|
+
def locate_list_file
|
216
|
+
@location = caller_locations.last.to_s.scan(/\d+/).last.to_i
|
217
|
+
@line = retrieve_file_data[@location - 1].strip.match(/\$l(?<line>.+)/)[:line][0...-1]
|
218
|
+
|
219
|
+
# if @line.is_a? Array
|
220
|
+
# @line.each_with_index do |list, idx|
|
221
|
+
# list.split[3] == iterable.to_s
|
222
|
+
# @line = list[0..list.index('end]') + 2] if list.split[3] == iterable.to_s
|
223
|
+
# end
|
224
|
+
# end
|
225
|
+
@line
|
226
|
+
|
227
|
+
end
|
129
228
|
end
|
229
|
+
$l = ListComprehension.instance
|
130
230
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_list_comprehension
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Michael
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: List Comprehensions for Ruby
|
14
14
|
email: smichael@appacademy.io
|