jop 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []