jop 0.0.2

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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/lib/jop.rb +308 -0
  3. data/lib/tokenizer.rb +61 -0
  4. metadata +46 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3297e329236a8838c0036f086ba8d4a871140ded
4
+ data.tar.gz: ff32cc6d6f4d0360814b3e16f4a7fdd1563286df
5
+ SHA512:
6
+ metadata.gz: 83334657122ade4b7cf4e8ccc0a101a10efee6595ffb2d633f3f115a861a7018b2bca42cabfbceeb76a2b1b4132063c52f4a3b163e7d118ab932052ea1e07992
7
+ data.tar.gz: aca459965e3c6fb141e5171ddccab6ee02eb39d3f4eb73491cdc10adad81202941cf04129f28673948db2505a35e774ee5a874d86c7df152ba225a7c4abf6b94
@@ -0,0 +1,308 @@
1
+
2
+ require 'tokenizer'
3
+
4
+ class Op
5
+ def numeric_literal? text
6
+ text =~ /^_?\d+/
7
+ end
8
+
9
+ def to_numeric text
10
+ return text.to_i if text =~ /^\d+/
11
+ -(text[1...text.length]).to_i
12
+ end
13
+
14
+ def integer_args interpreter
15
+ interpreter.tokens
16
+ .take_while {|n| numeric_literal?(n) }
17
+ .reverse
18
+ .map(&:to_i)
19
+ end
20
+
21
+ def apply_monad_deep element, &block
22
+ return yield element unless element.kind_of? Array
23
+ element.map {|e| apply_monad_deep(e, &block) }
24
+ end
25
+ end
26
+
27
+
28
+ class Ceil < Op; REP = '>.'
29
+ def run ary, interpreter
30
+ apply_monad_deep(ary) {|e| e.ceil }
31
+ end
32
+ end
33
+
34
+
35
+ class Complement < Op; REP = '-.'
36
+ def run ary, interpreter
37
+ apply_monad_deep(ary) {|e| 1 - e }
38
+ end
39
+ end
40
+
41
+
42
+ class Curtail < Op; REP = '}:'
43
+ def run ary, interpreter
44
+ ary.take(ary.count-1)
45
+ end
46
+ end
47
+
48
+
49
+ class Decrement < Op; REP = '<:'
50
+ def run ary, interpreter
51
+ apply_monad_deep(ary) {|e| e - 1 }
52
+ end
53
+ end
54
+
55
+
56
+ class Double < Op; REP = '+:'
57
+ def run ary, interpreter
58
+ apply_monad_deep(ary) {|e| e * 2 }
59
+ end
60
+ end
61
+
62
+
63
+ class Drop < Op; REP = '}.'
64
+ def run ary, interpreter
65
+ if interpreter.tokens.size > 0 && numeric_literal?(interpreter.tokens[0])
66
+ number = to_numeric(interpreter.tokens[0])
67
+ interpreter.advance(1)
68
+ if number >= 0
69
+ ary.drop(number)
70
+ else
71
+ ary.reverse.drop(-number).reverse
72
+ end
73
+ else
74
+ ary.drop(1)
75
+ end
76
+ end
77
+ end
78
+
79
+
80
+ class Exp < Op; REP = '^'
81
+ def run ary, interpreter
82
+ apply_monad_deep(ary) {|n| Math::exp(n) }
83
+ end
84
+ end
85
+
86
+
87
+ class Floor < Op; REP = '<.'
88
+ def run ary, interpreter
89
+ apply_monad_deep(ary) {|e| e.floor }
90
+ end
91
+ end
92
+
93
+
94
+ class GradeDown < Op; REP = '\:'
95
+ def run ary, interpreter
96
+ GradeUp.new.run(ary, interpreter).reverse
97
+ end
98
+ end
99
+
100
+
101
+ class GradeUp < Op; REP = '/:'
102
+ def run ary, interpreter
103
+ ary.zip(0...ary.length).sort_by {|e| e[0] }.map {|e| e[1] }
104
+ end
105
+ end
106
+
107
+
108
+ class Halve < Op; REP = '-:'
109
+ def run ary, interpreter
110
+ apply_monad_deep(ary) {|e| e / 2.0 }
111
+ end
112
+ end
113
+
114
+
115
+ class Increment < Op; REP = '>:'
116
+ def run ary, interpreter
117
+ apply_monad_deep(ary) {|e| e + 1 }
118
+ end
119
+ end
120
+
121
+
122
+ class Insert < Op; REP = '/'
123
+ def run ary, interpreter
124
+ if interpreter.tokens[0] == Plus::REP
125
+ interpreter.advance(1)
126
+ box ary.reduce(&method(:add))
127
+ elsif interpreter.tokens[0] == Sign::REP
128
+ interpreter.advance(1)
129
+ box ary.reduce(:*)
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ def add x, y
136
+ return x + y unless arrays? [x,y]
137
+ x.zip(y).map {|x,y| add(x,y) }
138
+ end
139
+
140
+ def box e
141
+ e.kind_of?(Array) ? e : [e]
142
+ end
143
+
144
+ def arrays? ary
145
+ ary.all? {|e| e.kind_of? Array }
146
+ end
147
+ end
148
+
149
+
150
+ class NoOp < Op; REP = ''
151
+ def run ary, interpreter
152
+ end
153
+ end
154
+
155
+
156
+ class Plus < Op; REP = '+'
157
+ def run ary, interpreter
158
+ args = integer_args(interpreter)
159
+ interpreter.advance(args.length)
160
+ args.empty? ? ary : ary.zip(args).map {|x,y| x + y }
161
+ end
162
+ end
163
+
164
+
165
+ class Reciprocal < Op; REP = '%'
166
+ def run ary, interpreter
167
+ apply_monad_deep(ary) {|e| 1 / e.to_f }
168
+ end
169
+ end
170
+
171
+
172
+ class ReverseRotate < Op; REP = '|.'
173
+ def run ary, interpreter
174
+ if interpreter.tokens.size > 0 && numeric_literal?(interpreter.tokens[0])
175
+ number = to_numeric(interpreter.tokens[0])
176
+ interpreter.advance(1)
177
+ segment_length = number % ary.length
178
+ segment = ary.take(segment_length)
179
+ ary.drop(segment_length) + segment
180
+ else
181
+ ary.reverse
182
+ end
183
+ end
184
+ end
185
+
186
+
187
+ class Shape < Op; REP = '$'
188
+ def run ary, interpreter
189
+ ranges = integer_args(interpreter)
190
+ interpreter.advance(ranges.length)
191
+ fill_matrix(ranges, ary.cycle.each)
192
+ end
193
+
194
+ private
195
+
196
+ def fill_matrix ranges, elements
197
+ return elements.next if ranges.size <= 0
198
+ (0...ranges.first).map { fill_matrix(ranges.drop(1), elements) }
199
+ end
200
+ end
201
+
202
+
203
+ class Sign < Op; REP = '*'
204
+ def run ary, interpreter
205
+ apply_monad_deep(ary) {|e| e <=> 0 }
206
+ end
207
+ end
208
+
209
+
210
+ class Square < Op; REP = '*:'
211
+ def run ary, interpreter
212
+ apply_monad_deep(ary) {|e| e ** 2 }
213
+ end
214
+ end
215
+
216
+
217
+ class Tail < Op; REP = '{:'
218
+ def run ary, interpreter
219
+ ary.drop(ary.count-1)
220
+ end
221
+ end
222
+
223
+
224
+ class Take < Op; REP = '{.'
225
+ def run ary, interpreter
226
+ if interpreter.tokens.size > 0 && numeric_literal?(interpreter.tokens[0])
227
+ number = to_numeric(interpreter.tokens[0])
228
+ interpreter.advance(1)
229
+ if number >= 0
230
+ ary.take(number)
231
+ else
232
+ ary.reverse.take(-number).reverse
233
+ end
234
+ else
235
+ ary.take(1)
236
+ end
237
+ end
238
+ end
239
+
240
+
241
+ class Tally < Op; REP = '#'
242
+ def run ary, interpreter
243
+ [ary.count]
244
+ end
245
+ end
246
+
247
+
248
+ class Tilde < Op; REP = '~'
249
+ def run ary, interpreter
250
+ if interpreter.tokens[0] == GradeUp::REP
251
+ interpreter.advance(1)
252
+ ary.sort
253
+ elsif interpreter.tokens[0] == GradeDown::REP
254
+ interpreter.advance(1)
255
+ ary.sort.reverse
256
+ end
257
+ end
258
+ end
259
+
260
+
261
+ class Jop
262
+ attr_reader :tokens
263
+
264
+ def initialize command_text
265
+ @command_text = command_text
266
+ @tokens = Tokenizer.new(command_text).tokens.reverse
267
+ end
268
+
269
+ def advance amount
270
+ @tokens = @tokens[amount...@tokens.length]
271
+ end
272
+
273
+ def eval_on ary
274
+ result = ary
275
+ while not @tokens.empty?
276
+ result = eval_op(result)
277
+ end
278
+ result
279
+ end
280
+
281
+ private
282
+
283
+ def operators
284
+ if not @operators
285
+ @operators = []
286
+ ObjectSpace.each_object(::Class) {|klass| @operators << klass.new if klass < Op }
287
+ end
288
+ @operators
289
+ end
290
+
291
+ def eval_op ary
292
+ token = @tokens[0]
293
+ advance(1)
294
+ operators.detect(NoOp.new) {|op | op.class::REP == token }
295
+ .run(ary, self)
296
+ end
297
+ end
298
+
299
+ class Array
300
+ def j command_text
301
+ Jop.new(command_text).eval_on(self)
302
+ end
303
+ end
304
+
305
+
306
+
307
+
308
+
@@ -0,0 +1,61 @@
1
+
2
+
3
+
4
+ class Tokenizer
5
+ attr_reader :tokens, :stream
6
+
7
+ def operator? char
8
+ '~^-%*|<>\\{}+/#$'.include?(char)
9
+ end
10
+
11
+ def initialize text
12
+ @stream = text
13
+ @tokens = []
14
+
15
+ skip_whitespace
16
+ while tokenizing?
17
+ if operator?(current_char)
18
+ if '.:'.include?(next_char)
19
+ @tokens << current_char + next_char
20
+ advance(2)
21
+ else
22
+ @tokens << current_char
23
+ advance(1)
24
+ end
25
+ else
26
+ if stream =~ /^_?\d+/
27
+ match_data = /^_?\d+/.match(stream)
28
+ @tokens << match_data[0]
29
+ advance(match_data[0].length)
30
+ else
31
+ break
32
+ end
33
+ end
34
+ skip_whitespace
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def tokenizing?
41
+ @stream.length > 0
42
+ end
43
+
44
+ def current_char
45
+ @stream.length > 0 ? @stream[0] : ' '
46
+ end
47
+
48
+ def next_char
49
+ @stream.length > 1 ? @stream[1] : ' '
50
+ end
51
+
52
+ def advance amount
53
+ @stream = @stream[amount..-1]
54
+ end
55
+
56
+ def skip_whitespace
57
+ @stream.lstrip!
58
+ end
59
+
60
+
61
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Michael Feathers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Jop is a gem that adds operations of the J programming language to Ruby
14
+ arrays
15
+ email: michael.feathers@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/jop.rb
21
+ - lib/tokenizer.rb
22
+ homepage: http://rubygems.org/gems/jop
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 2.0.3
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: jop
46
+ test_files: []