rubish 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,47 @@
1
+
2
+ class Rubish::Repl
3
+ class << self
4
+ def repl
5
+ self.new.repl
6
+ end
7
+ end
8
+
9
+ def initialize
10
+ @scanner = RubyLex.new
11
+ end
12
+
13
+ def repl
14
+ raise "$stdin is not a tty device" unless $stdin.tty?
15
+ raise "readline is not available??" unless defined?(IRB::ReadlineInputMethod)
16
+ rl = IRB::ReadlineInputMethod.new
17
+
18
+ @scanner.set_prompt do |ltype, indent, continue, line_no|
19
+ # ltype is Delimiter type. In strings that are continued across a line break, %l will display the type of delimiter used to begin the string, so you'll know how to end it. The delimiter will be one of ", ', /, ], or `.
20
+ if ltype or indent > 0 or continue
21
+ p = ". "
22
+ else
23
+ p = "> "
24
+ end
25
+ if indent
26
+ p << " " * indent
27
+ end
28
+ rl.prompt = p
29
+ end
30
+
31
+ @scanner.set_input(rl)
32
+
33
+ @scanner.each_top_level_statement do |line,line_no|
34
+ begin
35
+ r = Rubish::Context.current.eval(line)
36
+ if r.is_a?(Rubish::Executable)
37
+ r.exec
38
+ elsif r != Rubish::Null
39
+ pp r
40
+ end
41
+ rescue StandardError, ScriptError => e
42
+ puts e
43
+ puts e.backtrace
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/rubish/sed.rb ADDED
@@ -0,0 +1,37 @@
1
+ class Rubish::Sed < Rubish::Streamer
2
+
3
+ def initialize(exe)
4
+ super(exe)
5
+ end
6
+
7
+ def q
8
+ self.quit
9
+ end
10
+
11
+ def s(regexp,sub)
12
+ line.sub!(regexp,sub)
13
+ return line
14
+ end
15
+
16
+ def gs(regexp,sub)
17
+ line.gsub!(regexp,sub)
18
+ return line
19
+ end
20
+
21
+ def p(string=nil)
22
+ self.puts(string || line)
23
+ end
24
+
25
+ private
26
+
27
+ def stream_begin
28
+ end
29
+
30
+ def init_line
31
+ end
32
+
33
+ def stream_end
34
+ end
35
+
36
+
37
+ end
@@ -0,0 +1,347 @@
1
+ # a Streamer wraps the output of an exectuable
2
+ #
3
+ # this implements the streaming abstraction for
4
+ # Rubish::{Sed,Awk}, corresponding to Unix
5
+ # Power Fools of similar names.
6
+ class Rubish::Streamer < Rubish::Executable
7
+
8
+ class Trigger
9
+ attr_accessor :inverted
10
+ def initialize(streamer,block,a,b,inverted=false)
11
+ @block = block
12
+ @streamer = streamer
13
+ raise "the first pattern can't be null" if a.nil?
14
+ @a = a
15
+ @b = b # could be nil. If so, this is a positioned trigger.
16
+ @inverted = inverted
17
+ @tripped = false
18
+ end
19
+
20
+ def call
21
+ if @b
22
+ @streamer.instance_eval(&@block) if range_trigger
23
+ else
24
+ @streamer.instance_eval(&@block) if position_trigger
25
+ end
26
+ end
27
+
28
+ def range_trigger
29
+ if @tripped
30
+ @tripped = !test(@b)
31
+ true
32
+ else
33
+ @tripped = test(@a)
34
+ end
35
+ end
36
+
37
+ def position_trigger
38
+ test(@a)
39
+ end
40
+
41
+ def test(trigger)
42
+ case trigger
43
+ when :eof, -1
44
+ @streamer.peek.empty?
45
+ when :bof, 1
46
+ @streamer.lineno == 1
47
+ when Integer
48
+ @streamer.lineno == trigger
49
+ when Regexp
50
+ !(@streamer.line =~ trigger).nil?
51
+ end
52
+ end
53
+ end
54
+ attr_accessor :line
55
+ attr_reader :output
56
+ attr_reader :lineno
57
+ attr_reader :buckets, :bucket_types
58
+ attr_reader :exe
59
+
60
+ def initialize(exe)
61
+ @exe = exe
62
+ @acts = []
63
+ @output = nil # the IO object that puts and pp should write to.
64
+ @buffer = [] # look ahead buffer for peek(n)
65
+ @line = nil # the current line ("pattern space" in sed speak)
66
+ @lineno = 0 # current line number
67
+ @interrupt = nil # a few methods could interrupt the sed process loop.
68
+ @buckets = {}
69
+ @bucket_types = {}
70
+ end
71
+
72
+ def puts(*args)
73
+ output.puts args
74
+ end
75
+
76
+ # redirect
77
+ def exec!
78
+ streamer = self
79
+ Rubish::Job::ThreadJob.new {
80
+ begin
81
+ result = nil
82
+ old_output = streamer.exe.o
83
+ output = Rubish::Executable::ExecutableIO.ios([streamer.o || Rubish::Context.current.o,"w"]).first
84
+ # ask exe to output to a pipe
85
+ streamer.exe.o { |input|
86
+ # input to streamer is the output of the executable
87
+ result = streamer.exec_with(input,output.io,nil)
88
+ }.exec
89
+ result
90
+ ensure
91
+ streamer.exe.o = old_output # restores the output of the old executable
92
+ output.close if output
93
+ end
94
+ }
95
+ end
96
+
97
+ def exec
98
+ exec!.wait
99
+ end
100
+
101
+ def pp(obj)
102
+ output.pp obj
103
+ end
104
+
105
+ ##################################################
106
+ # Line Buffer Handling Stuff
107
+
108
+ def exec_with(i,o,_e=nil)
109
+ raise "error stream shouldn't be used" if _e
110
+ @output = o
111
+ @input = i
112
+ begin
113
+ stream_begin # abstract
114
+ while string = get_string
115
+ @line = string
116
+ init_line # abstract
117
+ interrupted = true
118
+ catch :interrupt do
119
+ @acts.each do |act|
120
+ # evaluate in the context of the object that included the Streamer.
121
+ if act.is_a?(Trigger)
122
+ act.call
123
+ else
124
+ self.instance_eval(&act)
125
+ end
126
+ end
127
+ interrupted = false
128
+ end
129
+ if interrupted
130
+ case @interrupt
131
+ when :quit
132
+ break # stop processing
133
+ when :done
134
+ next # restart loop, skip other actions
135
+ else
136
+ raise "Unknown Sed Interrupt: #{@interrupt}"
137
+ end
138
+ end
139
+ end
140
+ ensure
141
+ result = stream_end # abstract
142
+ end
143
+ return result
144
+ end
145
+
146
+ def stream_begin
147
+ raise "abstract"
148
+ end
149
+
150
+ def init_line
151
+ raise "abstract"
152
+ end
153
+
154
+ # the return value of this method is taken to be
155
+ # the return value of process_stream
156
+ def stream_end
157
+ raise "abstract"
158
+ end
159
+
160
+ def act(a=nil,b=nil,&block)
161
+ if a || b
162
+ @acts << Trigger.new(self,block,a,b)
163
+ else
164
+ @acts << block
165
+ end
166
+ return self
167
+ end
168
+
169
+ def interrupt(cmd=nil)
170
+ @interrupt = cmd
171
+ throw :interrupt
172
+ end
173
+
174
+ # returns line and advances the cursor.
175
+ # nil if EOF.
176
+ def get_string
177
+ # use line in lookahead buffer if there's any
178
+ if @buffer.empty?
179
+ r = @input.gets
180
+ r.chomp! if r
181
+ else
182
+ r = @buffer.shift
183
+ end
184
+ @lineno += 1 if r # increments lineno iff it's not EOF
185
+ return r
186
+ end
187
+
188
+ # peek(n) returns n (or less, if we reach EOF)
189
+ # lines from the cursor without advancing it.
190
+ def peek(n=1)
191
+ lines = @buffer[0...n]
192
+ # return if we have enough lines in buffer to satisfy peek.
193
+ return lines if lines.length == n
194
+ # or keep reading from the pipe if we don't.
195
+ n = n - lines.length
196
+ n.times do |i|
197
+ s = @input.gets
198
+ break if s.nil? # EOF
199
+ s.chomp!
200
+ lines << s
201
+ @buffer << s
202
+ end
203
+ return lines
204
+ end
205
+
206
+ # advances cursor by n
207
+ # returns false if EOF
208
+ # returns true otherwise.
209
+ def skip(n=1)
210
+ n.times do
211
+ return false unless get_string
212
+ end
213
+ return !peek.nil?
214
+ end
215
+
216
+ # skip other actions
217
+ def done
218
+ interrupt(:done)
219
+ end
220
+
221
+ def quit
222
+ interrupt(:quit)
223
+ end
224
+
225
+ ##################################################
226
+ # Bucket Handling Stuff
227
+
228
+
229
+ # common-lisp loopesque helpers
230
+ def count(name,key=nil)
231
+ create_bucket(:count,name,0)
232
+ update_bucket(name,[key,nil]) do |old_c,ignore|
233
+ old_c + 1
234
+ end
235
+ end
236
+
237
+ def pick(name,val,key=nil)
238
+ create_bucket(:pick,name,nil)
239
+ update_bucket(name,val,key) do |old_v,new_v|
240
+ if old_v.nil?
241
+ new_v
242
+ else
243
+ yield(old_v,new_v)
244
+ end
245
+ end
246
+ end
247
+
248
+ def max(name,val,key=nil)
249
+ create_bucket(:max,name,nil)
250
+ update_bucket(name,val,key) do |old,new|
251
+ if old.nil?
252
+ new
253
+ elsif new > old
254
+ new
255
+ else
256
+ old
257
+ end
258
+ end
259
+ end
260
+
261
+ def min(name,val,key=nil)
262
+ create_bucket(:min,name,nil)
263
+ update_bucket(name,val,key) do |old,new|
264
+ if old.nil?
265
+ new
266
+ elsif new < old
267
+ new
268
+ else
269
+ old
270
+ end
271
+ end
272
+ end
273
+
274
+ def collect(name,val,key=nil)
275
+ # the initial value should be nil, if it's the
276
+ # empty array, it would be shared among all
277
+ # the buckets (which is incorrect (since we
278
+ # are doing destructive append))
279
+ create_bucket(:collect,name,nil)
280
+ update_bucket(name,val,key) do |acc,val|
281
+ if acc.nil?
282
+ acc = [val]
283
+ else
284
+ acc << val
285
+ end
286
+ acc
287
+ end
288
+ end
289
+
290
+ # size-limited FIFO buffer
291
+ def hold(name,size,val,key=nil)
292
+ raise "hold size should be larger than 1" unless size >= 1
293
+ create_bucket(:hold,name,nil)
294
+ update_bucket(name,val,key) do |acc,val|
295
+ if acc.nil?
296
+ acc = [val]
297
+ elsif acc.length < size
298
+ acc << val
299
+ else
300
+ acc.shift
301
+ acc << val
302
+ end
303
+ acc
304
+ end
305
+ end
306
+
307
+ private
308
+
309
+ # [type] denotes an aggregate of type
310
+ # type denotes the type itself (non-aggregate)
311
+ def create_bucket(type,name,init_val)
312
+ name = name.to_sym
313
+ if buckets.has_key?(name)
314
+ raise "conflict bucket types for: #{name}" unless bucket_types[name] == type
315
+ return false
316
+ else
317
+ raise "bucket name conflicts with existing method: #{name}" if self.respond_to?(name)
318
+ bucket_types[name] = type
319
+ buckets[name] = Hash.new(init_val)
320
+ #singleton = class << self; self; end
321
+
322
+ defstr = <<-HERE
323
+ def #{name.to_s}(key=nil)
324
+ buckets[:#{name.to_s}][key]
325
+ end
326
+ HERE
327
+ self.instance_eval(defstr)
328
+
329
+
330
+ end
331
+ return true
332
+ end
333
+
334
+ def update_bucket(name,val,key=nil)
335
+ name = name.to_sym
336
+ # if a key is given, update the key specific sub-bucket.
337
+ if key
338
+ new_val = yield(buckets[name][key],val)
339
+ buckets[name][key] = new_val
340
+ end
341
+ # always update the special nil key.
342
+ new_val = yield(buckets[name][nil],val)
343
+ buckets[name][nil] = new_val
344
+ end
345
+
346
+
347
+ end
@@ -0,0 +1,83 @@
1
+
2
+ module Rubish
3
+ # magic singleton value to supress shell output.
4
+ module Null
5
+ end
6
+
7
+ class Error < RuntimeError
8
+ end
9
+
10
+ class << self
11
+ def repl
12
+ Repl.repl
13
+ end
14
+
15
+ # dup2 the given i,o,e to stdin,stdout,stderr
16
+ # close all other file descriptors.
17
+ def set_stdioe(i,o,e)
18
+ $stdin.reopen(i)
19
+ $stdout.reopen(o)
20
+ $stderr.reopen(e)
21
+ ObjectSpace.each_object(IO) do |io|
22
+ unless io.closed? || [0,1,2].include?(io.fileno)
23
+ io.close
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def Rubish(&__block)
31
+ Rubish::Context.global.eval {
32
+ begin
33
+ self.eval(&__block)
34
+ ensure
35
+ waitall
36
+ end
37
+ }
38
+ end
39
+
40
+
41
+ # Rubish::UnixExecutable < Rubish::Executable
42
+ # Rubish::Command < Rubish::UnixExecutable
43
+ # Rubish::Pipe < Rubish::UnixExecutable
44
+ #
45
+ # Rubish::Streamer < Rubish::Executable
46
+ # Rubish::Sed < Rubish::Streamer
47
+ # Rubish::Awk < Rubish::Streamer
48
+ #
49
+ # Rubish::BatchExecutable < Rubish::Executable
50
+ class Rubish::Executable
51
+ # stub, see: executable.rb
52
+ def exec
53
+ raise "implemented in executable.rb"
54
+ end
55
+ end
56
+
57
+ # This is an object that doesn't respond to anything.
58
+ #
59
+ # This provides an empty context for instance_eval (__instance_eval
60
+ # for the Mu object). It catches all method calls with method_missing.
61
+ # It is All and Nothing.
62
+ class Rubish::Mu
63
+
64
+ self.public_instance_methods.each do |m|
65
+ # for consistency's sake, methods already
66
+ # underscored should also be aliased, but we
67
+ # don't undefine it.
68
+ self.send(:alias_method,"__#{m}",m)
69
+ if m[0..1] != "__"
70
+ # don't remove special methods (i.e. __id__, __send__)
71
+ self.send(:undef_method,m)
72
+ end
73
+ end
74
+
75
+ def initialize(*modules,&block)
76
+ raise "abstract"
77
+ end
78
+
79
+ def method_missing(*args,&block)
80
+ raise "abstract"
81
+ end
82
+
83
+ end