mucgly 0.0.1
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.
- data/CHANGELOG.rdoc +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +292 -0
- data/Rakefile +29 -0
- data/bin/mucgly +220 -0
- data/doc/EasyFile/InOut.html +2097 -0
- data/doc/EasyFile/Read.html +1334 -0
- data/doc/EasyFile/ReadStack.html +461 -0
- data/doc/EasyFile/Stacked.html +411 -0
- data/doc/EasyFile/String.html +570 -0
- data/doc/EasyFile/Write.html +1084 -0
- data/doc/EasyFile/WriteStack.html +305 -0
- data/doc/EasyFile.html +155 -0
- data/doc/Mucgly/Env.html +1675 -0
- data/doc/Mucgly/MucglyFile/ParseState.html +1662 -0
- data/doc/Mucgly/MucglyFile/Token.html +529 -0
- data/doc/Mucgly/MucglyFile.html +545 -0
- data/doc/Mucgly/Separators.html +521 -0
- data/doc/Mucgly.html +244 -0
- data/doc/_index.html +261 -0
- data/doc/class_list.html +53 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +338 -0
- data/doc/file.CHANGELOG.html +79 -0
- data/doc/file.README.html +390 -0
- data/doc/file_list.html +58 -0
- data/doc/frames.html +28 -0
- data/doc/index.html +390 -0
- data/doc/js/app.js +214 -0
- data/doc/js/full_list.js +178 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +742 -0
- data/doc/top-level-namespace.html +112 -0
- data/lib/easyfile.rb +720 -0
- data/lib/mucgly.rb +627 -0
- data/test/test_basic.rx.txt +19 -0
- data/test/test_include.rb +6 -0
- data/test/test_include.txt +3 -0
- data/test/test_mucgly.rb +32 -0
- data/test/test_multi.rx.txt +4 -0
- data/test/test_specials_cli.rx.txt +11 -0
- data/test/test_specials_cmd.rx.txt +53 -0
- metadata +98 -0
data/lib/easyfile.rb
ADDED
@@ -0,0 +1,720 @@
|
|
1
|
+
# EasyFile module provides simplified file access management. The user
|
2
|
+
# can refer all files by name, also the standard streams (STDIN,
|
3
|
+
# STDOUT, STDERR). File names are unique since they are stored as
|
4
|
+
# absolute path names. Also short names (relative/original) can be
|
5
|
+
# used.
|
6
|
+
#
|
7
|
+
# The files are divided to Read and Write files. This simplifies the file
|
8
|
+
# handling. Both files maintain the current line number.
|
9
|
+
#
|
10
|
+
# Read files has put-back feature. Arbitrary number of chars can be put
|
11
|
+
# back to stream. This is helpful for parsers that has to perform
|
12
|
+
# look-ahead.
|
13
|
+
#
|
14
|
+
# Write file have redirection support. Write file can be split into two
|
15
|
+
# physical files if output requires copying to two or more files. Two
|
16
|
+
# (or more ) Write files can also be joined, i.e. all writes are
|
17
|
+
# directed to the joined file. Stream output can be suppressed.
|
18
|
+
#
|
19
|
+
# Examples:
|
20
|
+
# f = EasyFile::Read.open( 'myfile' )
|
21
|
+
# c = f.getc
|
22
|
+
# if c == "#"
|
23
|
+
# puts "Error in #{f.absname} on line #{f.line+1}: can't have '#' as first char..."
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# f = EasyFile::Write.open( 'myfile' )
|
27
|
+
# f.branch( 'myfile2' )
|
28
|
+
# f.puts "foo\nbar\ndii"
|
29
|
+
# puts "Number of lines output to 'myfile' and 'myfile2': #{f.line}..."
|
30
|
+
#
|
31
|
+
|
32
|
+
module EasyFile
|
33
|
+
|
34
|
+
|
35
|
+
# Base class for Read and Write files.
|
36
|
+
class InOut
|
37
|
+
|
38
|
+
require 'stringio'
|
39
|
+
|
40
|
+
# Map symbolic file direction to mode character.
|
41
|
+
FILEMODEMAP = {
|
42
|
+
:read => 'r',
|
43
|
+
:write => 'w',
|
44
|
+
}
|
45
|
+
|
46
|
+
# Get absolute path for name.
|
47
|
+
#
|
48
|
+
# @param name [String] Relative file name.
|
49
|
+
# @return [String] Absolute path name for the file.
|
50
|
+
def InOut.abspath( name, mode )
|
51
|
+
if InOut.standard?( name )
|
52
|
+
name
|
53
|
+
elsif mode == :string
|
54
|
+
name
|
55
|
+
else
|
56
|
+
File.absolute_path( name )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Is name for standard IO?
|
61
|
+
#
|
62
|
+
# @param name [String] Relative file name.
|
63
|
+
def InOut.standard?( name )
|
64
|
+
case name
|
65
|
+
when "<STDIN>"; true
|
66
|
+
when "<STDOUT>"; true
|
67
|
+
when "<STDERR>"; true
|
68
|
+
else false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Create EasyFile object and, open file with given name and
|
74
|
+
# mode.
|
75
|
+
#
|
76
|
+
# @param name [String] Relative path name for file.
|
77
|
+
# @param mode [Symbol] Access direction (:read/:write).
|
78
|
+
# @return [Read,Write] EasyFile handle.
|
79
|
+
def InOut.open( name, mode = :read )
|
80
|
+
file = InOut.create( name, mode )
|
81
|
+
file.open
|
82
|
+
file
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# Create EasyFile object, no open. Open is performed
|
87
|
+
# separately.
|
88
|
+
#
|
89
|
+
# @param name [String] Relative path name for file.
|
90
|
+
# @param mode [Symbol] Access direction (:read/:write).
|
91
|
+
# @return [Read,Write] EasyFile handle to non-opened file (use
|
92
|
+
# "open").
|
93
|
+
def InOut.create( name, mode = :read )
|
94
|
+
|
95
|
+
absname = InOut.abspath( name, mode )
|
96
|
+
|
97
|
+
# Check if file exists already.
|
98
|
+
file = InOut.getByAbsname( absname )
|
99
|
+
|
100
|
+
if file
|
101
|
+
|
102
|
+
file
|
103
|
+
|
104
|
+
else
|
105
|
+
|
106
|
+
if mode == :read
|
107
|
+
# file = Read.new( name, absname )
|
108
|
+
file = Read.new( name, absname )
|
109
|
+
elsif mode == :write
|
110
|
+
file = Write.new( name, absname )
|
111
|
+
elsif mode == :string
|
112
|
+
file = String.new( name, absname )
|
113
|
+
else
|
114
|
+
raise RuntimeError, "Unknown file mode \"#{mode.to_s}\"..."
|
115
|
+
end
|
116
|
+
|
117
|
+
InOut.add( file )
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
file
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
# Add InOut to filelist.
|
127
|
+
#
|
128
|
+
# @param io [InOut] Item to list.
|
129
|
+
# @return [InOut] Added item.
|
130
|
+
def InOut.add( io )
|
131
|
+
@@file_io[ io.absname ] = io
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# Delete InOut from filelist.
|
136
|
+
#
|
137
|
+
# @param io [InOut] Item to list.
|
138
|
+
# @return [InOut] Added item.
|
139
|
+
def InOut.delete( io )
|
140
|
+
@@file_io.delete( io.absname )
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
# Get InOut from filelist by InOut handle.
|
145
|
+
#
|
146
|
+
# @param io [InOut] Item handle.
|
147
|
+
# @return [InOut] Added item.
|
148
|
+
def InOut.get( io )
|
149
|
+
@@file_io.getByAbsname( io.absname )
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
# Get InOut from filelist by absname.
|
154
|
+
#
|
155
|
+
# @param absname [InOut] Item absname.
|
156
|
+
# @return [InOut] Added item.
|
157
|
+
def InOut.getByAbsname( absname )
|
158
|
+
@@file_io[ absname ]
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
# Get EasyFile list.
|
163
|
+
#
|
164
|
+
# @return [Array<InOut>] List of all files.
|
165
|
+
def InOut.getList
|
166
|
+
@@file_io.values
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
|
171
|
+
# Relative path name for file.
|
172
|
+
attr_reader :filename
|
173
|
+
|
174
|
+
# Absolute path name for file.
|
175
|
+
attr_reader :absname
|
176
|
+
alias :id :absname
|
177
|
+
|
178
|
+
# File mode.
|
179
|
+
attr_reader :mode
|
180
|
+
|
181
|
+
# IO stream handle.
|
182
|
+
attr_accessor :io
|
183
|
+
|
184
|
+
# Parent EasyFile (push/pop).
|
185
|
+
attr_accessor :parent
|
186
|
+
|
187
|
+
# List of peer files (braches).
|
188
|
+
attr_accessor :peers
|
189
|
+
|
190
|
+
# Current line number (0,1,2...)
|
191
|
+
attr_accessor :line
|
192
|
+
|
193
|
+
|
194
|
+
# Create InOut object.
|
195
|
+
#
|
196
|
+
# @param mode [Symbol] File operation mode (:read, :write, :string).
|
197
|
+
# @param filename [String] Relative path file name.
|
198
|
+
# @param absname [String] Absolute path file name (generated
|
199
|
+
# if not given).
|
200
|
+
# @param io [IO] IO handle.
|
201
|
+
def initialize( mode, filename, absname = nil, io = nil )
|
202
|
+
|
203
|
+
@mode = mode
|
204
|
+
|
205
|
+
unless absname
|
206
|
+
absname = InOut.abspath( filename, mode )
|
207
|
+
end
|
208
|
+
|
209
|
+
# Set Primary IO.
|
210
|
+
@io = io
|
211
|
+
|
212
|
+
@filename = filename
|
213
|
+
@absname = absname
|
214
|
+
|
215
|
+
@peers = []
|
216
|
+
|
217
|
+
@line = 0
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
# Open the stream.
|
222
|
+
def open
|
223
|
+
|
224
|
+
# Ensure single open.
|
225
|
+
return if self.io
|
226
|
+
|
227
|
+
if mode == :string
|
228
|
+
handle = StringIO.new
|
229
|
+
else
|
230
|
+
handle = File.open( @absname, FILEMODEMAP[ mode ] )
|
231
|
+
end
|
232
|
+
|
233
|
+
self.io = handle
|
234
|
+
|
235
|
+
self
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
# Close primary stream and peer streams.
|
240
|
+
def close
|
241
|
+
|
242
|
+
begin
|
243
|
+
unless InOut.standard?( @filename )
|
244
|
+
@io.close if @io
|
245
|
+
InOut.delete( self )
|
246
|
+
end
|
247
|
+
rescue IOError
|
248
|
+
end
|
249
|
+
|
250
|
+
@peers.each do |i|
|
251
|
+
i.close
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
private
|
257
|
+
|
258
|
+
# Update line number status according to encountered newline
|
259
|
+
# chars.
|
260
|
+
#
|
261
|
+
# @param blk [String] Transfer chars.
|
262
|
+
# @param offset [Integer] Line number value Change step.
|
263
|
+
def updateLineNumber( blk, offset = 1 )
|
264
|
+
return nil unless blk
|
265
|
+
cnt = 0
|
266
|
+
blk.each_char do |c|
|
267
|
+
cnt += offset if c == "\n"
|
268
|
+
end
|
269
|
+
@line += cnt
|
270
|
+
blk
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
# Read file.
|
278
|
+
#
|
279
|
+
# Features:
|
280
|
+
# * Line counting.
|
281
|
+
# * Put-back char support.
|
282
|
+
class Read < InOut
|
283
|
+
|
284
|
+
|
285
|
+
# Create EasyFile::Read object and, open file with given name.
|
286
|
+
#
|
287
|
+
# @param name [String] Relative path name for file.
|
288
|
+
# @return [Read] EasyFile handle.
|
289
|
+
def Read.open( name )
|
290
|
+
InOut.open( name, :read )
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
# Create Read object.
|
295
|
+
#
|
296
|
+
# @param filename [String] Relative path file name.
|
297
|
+
# @param absname [String] Absolute path file name (generated
|
298
|
+
# if not given).
|
299
|
+
# @param io [IO] IO handle.
|
300
|
+
def initialize( filename, absname = nil, io = nil )
|
301
|
+
super( :read, filename, absname, io )
|
302
|
+
|
303
|
+
@charbuffer = ""
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
# Get line.
|
308
|
+
def gets
|
309
|
+
begin
|
310
|
+
readline
|
311
|
+
rescue EOFError
|
312
|
+
nil
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
|
317
|
+
# Get line without newline.
|
318
|
+
def getschomp
|
319
|
+
line = gets
|
320
|
+
if line
|
321
|
+
line.chomp
|
322
|
+
else
|
323
|
+
line
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
|
328
|
+
# Get char (from IO or put-back buffer).
|
329
|
+
def getc
|
330
|
+
begin
|
331
|
+
getchar
|
332
|
+
rescue EOFError
|
333
|
+
nil
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
|
338
|
+
# Get char (from IO or put-back buffer).
|
339
|
+
def getchar
|
340
|
+
updateLineNumber( getn( 1 ) )
|
341
|
+
end
|
342
|
+
|
343
|
+
|
344
|
+
# Put-back char(s) (to put-back buffer).
|
345
|
+
def putback( c )
|
346
|
+
@charbuffer = c + @charbuffer
|
347
|
+
updateLineNumber( c, -1 )
|
348
|
+
end
|
349
|
+
|
350
|
+
|
351
|
+
# Read from IO.
|
352
|
+
def read( length = 1024 )
|
353
|
+
begin
|
354
|
+
readchars( length )
|
355
|
+
rescue EOFError
|
356
|
+
nil
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
|
361
|
+
# Read from IO with exception.
|
362
|
+
def readchars( length = 1024 )
|
363
|
+
updateLineNumber( getn( length ) )
|
364
|
+
end
|
365
|
+
|
366
|
+
|
367
|
+
def eof?
|
368
|
+
@charbuffer.empty? && @io.eof?
|
369
|
+
end
|
370
|
+
|
371
|
+
alias eof eof?
|
372
|
+
|
373
|
+
|
374
|
+
# Return Array of lines.
|
375
|
+
def readlines( chomp = false )
|
376
|
+
lines = []
|
377
|
+
if chomp
|
378
|
+
loop do
|
379
|
+
line = getschomp
|
380
|
+
break unless line
|
381
|
+
lines.push line
|
382
|
+
end
|
383
|
+
else
|
384
|
+
loop do
|
385
|
+
line = gets
|
386
|
+
break unless line
|
387
|
+
lines.push line
|
388
|
+
end
|
389
|
+
end
|
390
|
+
lines
|
391
|
+
end
|
392
|
+
|
393
|
+
# Iterate over lines.
|
394
|
+
#
|
395
|
+
# @yield Each line.
|
396
|
+
def each_line( &blk )
|
397
|
+
while ( line = gets )
|
398
|
+
yield line
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
|
403
|
+
# Iterate over lines with newline removed.
|
404
|
+
#
|
405
|
+
# @yield Each line.
|
406
|
+
def each_chomp( &blk )
|
407
|
+
while ( line = getschomp )
|
408
|
+
yield line
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
|
413
|
+
# Readline from file.
|
414
|
+
def readline
|
415
|
+
updateLineNumber( getline )
|
416
|
+
end
|
417
|
+
|
418
|
+
|
419
|
+
|
420
|
+
private
|
421
|
+
|
422
|
+
# Get n characters from stream.
|
423
|
+
def getn( n = 1 )
|
424
|
+
|
425
|
+
while n > @charbuffer.length
|
426
|
+
# Fill char buffer first.
|
427
|
+
break if @io.eof?
|
428
|
+
@charbuffer += @io.readline
|
429
|
+
end
|
430
|
+
|
431
|
+
ret = @charbuffer[ 0..(n-1) ]
|
432
|
+
|
433
|
+
raise EOFError if ret == ""
|
434
|
+
|
435
|
+
@charbuffer = @charbuffer[ n..-1 ]
|
436
|
+
|
437
|
+
unless @charbuffer
|
438
|
+
@charbuffer = ""
|
439
|
+
end
|
440
|
+
|
441
|
+
ret
|
442
|
+
end
|
443
|
+
|
444
|
+
|
445
|
+
# Get chars until newline.
|
446
|
+
def getline
|
447
|
+
|
448
|
+
if @charbuffer.empty?
|
449
|
+
@io.readline
|
450
|
+
else
|
451
|
+
ret = ""
|
452
|
+
nl = nil
|
453
|
+
|
454
|
+
# Check if newline in @charbuffer.
|
455
|
+
if ( nl = @charbuffer.index( "\n" ) )
|
456
|
+
ret = @charbuffer[ 0..nl ]
|
457
|
+
@charbuffer = @charbuffer[ nl+1..-1 ]
|
458
|
+
ret
|
459
|
+
else
|
460
|
+
if @io.eof?
|
461
|
+
ret = @charbuffer
|
462
|
+
else
|
463
|
+
ret = @charbuffer + @io.readline
|
464
|
+
end
|
465
|
+
@charbuffer = ""
|
466
|
+
ret
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
end
|
472
|
+
|
473
|
+
|
474
|
+
|
475
|
+
# Write file.
|
476
|
+
#
|
477
|
+
# Features:
|
478
|
+
# * Line counting.
|
479
|
+
# * Branching and joining.
|
480
|
+
# * Stream output suppression.
|
481
|
+
class Write < InOut
|
482
|
+
|
483
|
+
|
484
|
+
# Create EasyFile::Write object and, open file with given name.
|
485
|
+
#
|
486
|
+
# @param name [String] Relative path name for file.
|
487
|
+
# @return [Write] EasyFile handle.
|
488
|
+
def Write.open( name )
|
489
|
+
InOut.open( name, :write )
|
490
|
+
end
|
491
|
+
|
492
|
+
|
493
|
+
# Output is enabled.
|
494
|
+
attr_accessor :enabled
|
495
|
+
|
496
|
+
# Flush after each write.
|
497
|
+
attr_accessor :flush
|
498
|
+
|
499
|
+
|
500
|
+
# Create Write object.
|
501
|
+
#
|
502
|
+
# @param filename [String] Relative path file name.
|
503
|
+
# @param absname [String] Absolute path file name (generated
|
504
|
+
# if not given).
|
505
|
+
# @param io [IO] IO handle.
|
506
|
+
def initialize( filename, absname = nil, io = nil )
|
507
|
+
super( :write, filename, absname, io )
|
508
|
+
@enabled = true
|
509
|
+
@flush = false
|
510
|
+
end
|
511
|
+
|
512
|
+
|
513
|
+
# Write to all IOs.
|
514
|
+
def write( str )
|
515
|
+
if @enabled
|
516
|
+
updateLineNumber( str )
|
517
|
+
if @io
|
518
|
+
@io.write( str )
|
519
|
+
@io.flush if @flush
|
520
|
+
else
|
521
|
+
# No output with nil stream.
|
522
|
+
end
|
523
|
+
|
524
|
+
@peers.each do |p|
|
525
|
+
p.write( str )
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
|
531
|
+
# Write with automatic newline.
|
532
|
+
def puts( str )
|
533
|
+
nl = str[-1] == "\n" ? '' : "\n"
|
534
|
+
write( str + nl )
|
535
|
+
end
|
536
|
+
|
537
|
+
|
538
|
+
# Attach a file to existing Write stream i.e. writes to Write
|
539
|
+
# will be directed to attached file as well.
|
540
|
+
#
|
541
|
+
# @param fileid [String,Write] Filename or Write to add branching.
|
542
|
+
# @return [Write] Added peer.
|
543
|
+
def branch( fileid )
|
544
|
+
|
545
|
+
if fileid.class == String
|
546
|
+
|
547
|
+
|
548
|
+
# Get new IO.
|
549
|
+
fileio = Write.open( fileid )
|
550
|
+
|
551
|
+
else
|
552
|
+
|
553
|
+
fileio = fileid
|
554
|
+
|
555
|
+
end
|
556
|
+
|
557
|
+
# Add if not already included.
|
558
|
+
@peers.push fileio unless @peers.index( fileio )
|
559
|
+
|
560
|
+
fileio
|
561
|
+
end
|
562
|
+
|
563
|
+
|
564
|
+
# Join the given filestream to this stream. After joining the
|
565
|
+
# joined stream is output to same file as this stream.
|
566
|
+
#
|
567
|
+
# @param fileid [String,Write] Filename or Write to join.
|
568
|
+
# @return [Write] Joined peer.
|
569
|
+
def join( fileid )
|
570
|
+
|
571
|
+
if fileid.class == String
|
572
|
+
fileio = Write.create( fileid, nil )
|
573
|
+
else
|
574
|
+
fileio = fileid
|
575
|
+
fileio.close
|
576
|
+
end
|
577
|
+
|
578
|
+
fileio.io = @io
|
579
|
+
fileio
|
580
|
+
end
|
581
|
+
|
582
|
+
|
583
|
+
# Suppress stream output.
|
584
|
+
#
|
585
|
+
# @param disable [Boolean] Disable or enable suppression.
|
586
|
+
def suppress( disable = true )
|
587
|
+
@enabled = !disable
|
588
|
+
end
|
589
|
+
|
590
|
+
end
|
591
|
+
|
592
|
+
|
593
|
+
# String buffer file.
|
594
|
+
#
|
595
|
+
# Features:
|
596
|
+
# * Putback.
|
597
|
+
class String < InOut
|
598
|
+
|
599
|
+
def method_missing( name, *args, &block )
|
600
|
+
@io.send( name, *args, &block )
|
601
|
+
end
|
602
|
+
|
603
|
+
|
604
|
+
# Create EasyFile::String object and, open file with given name.
|
605
|
+
#
|
606
|
+
# @param name [String] Name for file.
|
607
|
+
# @return [String] EasyFile handle.
|
608
|
+
def String.open( name )
|
609
|
+
InOut.open( name, :string )
|
610
|
+
end
|
611
|
+
|
612
|
+
# Create String object.
|
613
|
+
#
|
614
|
+
# @param filename [String] Name.
|
615
|
+
# @param absname [String] Absolute path file name (generated
|
616
|
+
# if not given).
|
617
|
+
# @param io [IO] IO handle.
|
618
|
+
def initialize( filename, absname = nil, io = nil )
|
619
|
+
super( :string, filename, absname, io )
|
620
|
+
end
|
621
|
+
|
622
|
+
|
623
|
+
# Put-back char(s) (to put-back buffer).
|
624
|
+
# @param c [String] Chars to put back to buffer.
|
625
|
+
def putback( c )
|
626
|
+
@io.string = c + @io.string[ @io.pos..-1 ]
|
627
|
+
end
|
628
|
+
|
629
|
+
|
630
|
+
end
|
631
|
+
|
632
|
+
|
633
|
+
class InOut
|
634
|
+
|
635
|
+
# Declare standards IOs.
|
636
|
+
@@file_io = {
|
637
|
+
"<STDIN>" => Read.new( '<STDIN>', nil, STDIN ),
|
638
|
+
"<STDOUT>" => Write.new( '<STDOUT>', nil, STDOUT ),
|
639
|
+
"<STDERR>" => Write.new( '<STDERR>', nil, STDERR ),
|
640
|
+
# filename (absname) => InOut object
|
641
|
+
}
|
642
|
+
|
643
|
+
end
|
644
|
+
|
645
|
+
|
646
|
+
# Stack of files.
|
647
|
+
module Stacked
|
648
|
+
attr_accessor :stack
|
649
|
+
|
650
|
+
instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$)/ }
|
651
|
+
|
652
|
+
def setup( first = nil )
|
653
|
+
if first
|
654
|
+
@stack = [ first ]
|
655
|
+
else
|
656
|
+
@stack = []
|
657
|
+
end
|
658
|
+
end
|
659
|
+
|
660
|
+
def pop
|
661
|
+
if @stack.length > 1
|
662
|
+
@stack[-1].close
|
663
|
+
@stack.pop
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
def method_missing( name, *args, &block )
|
668
|
+
if [ :getc, :gets, :gets! ].index( name )
|
669
|
+
if @stack[-1].eof? && @automode
|
670
|
+
pop
|
671
|
+
end
|
672
|
+
end
|
673
|
+
@stack[-1].send( name, *args, &block )
|
674
|
+
end
|
675
|
+
|
676
|
+
alias close pop
|
677
|
+
end
|
678
|
+
|
679
|
+
|
680
|
+
# Stack of Read files.
|
681
|
+
class ReadStack
|
682
|
+
include Stacked
|
683
|
+
|
684
|
+
# Automatically revert to lower file at EOF.
|
685
|
+
attr_accessor :automode
|
686
|
+
|
687
|
+
def initialize( name )
|
688
|
+
io = InOut.open( name, :read )
|
689
|
+
setup( io )
|
690
|
+
@automode = true
|
691
|
+
end
|
692
|
+
|
693
|
+
def push( name )
|
694
|
+
io = InOut.open( name, :read )
|
695
|
+
@stack.push io
|
696
|
+
end
|
697
|
+
|
698
|
+
def eof?
|
699
|
+
@stack.length == 1 && @stack[-1].eof?
|
700
|
+
end
|
701
|
+
end
|
702
|
+
|
703
|
+
|
704
|
+
# Stack of Write files.
|
705
|
+
class WriteStack
|
706
|
+
include Stacked
|
707
|
+
|
708
|
+
def initialize( name )
|
709
|
+
io = InOut.open( name, :write )
|
710
|
+
setup( io )
|
711
|
+
end
|
712
|
+
|
713
|
+
def push( name )
|
714
|
+
io = InOut.open( name, :write )
|
715
|
+
@stack.push io
|
716
|
+
end
|
717
|
+
|
718
|
+
end
|
719
|
+
|
720
|
+
end
|