antlr3 1.6.0 → 1.6.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -0
- data/Manifest.txt +94 -0
- data/README.txt +1 -1
- data/Rakefile +58 -0
- data/bin/antlr4ruby +101 -7
- data/java/antlr-full-3.2.1.jar +0 -0
- data/lib/antlr3.rb +38 -10
- data/lib/antlr3/constants.rb +13 -5
- data/lib/antlr3/debug.rb +57 -57
- data/lib/antlr3/dfa.rb +138 -68
- data/lib/antlr3/dot.rb +32 -32
- data/lib/antlr3/error.rb +85 -78
- data/lib/antlr3/main.rb +191 -187
- data/lib/antlr3/profile.rb +71 -70
- data/lib/antlr3/recognizers.rb +261 -226
- data/lib/antlr3/streams.rb +85 -84
- data/lib/antlr3/streams/interactive.rb +20 -27
- data/lib/antlr3/streams/rewrite.rb +89 -89
- data/lib/antlr3/task.rb +42 -33
- data/lib/antlr3/template.rb +2 -2
- data/lib/antlr3/template/group-lexer.rb +1 -1
- data/lib/antlr3/token.rb +76 -68
- data/lib/antlr3/tree.rb +125 -121
- data/lib/antlr3/tree/visitor.rb +1 -1
- data/lib/antlr3/tree/wizard.rb +1 -1
- data/lib/antlr3/util.rb +32 -33
- data/lib/antlr3/version.rb +3 -3
- data/templates/Ruby.stg +1 -1
- data/test/unit/test-streams.rb +11 -10
- data/test/unit/test-template.rb +206 -204
- metadata +4 -2
data/lib/antlr3/streams.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
=begin LICENSE
|
5
5
|
|
6
6
|
[The "BSD licence"]
|
7
|
-
Copyright (c) 2009 Kyle Yetter
|
7
|
+
Copyright (c) 2009-2010 Kyle Yetter
|
8
8
|
All rights reserved.
|
9
9
|
|
10
10
|
Redistribution and use in source and binary forms, with or without
|
@@ -251,7 +251,8 @@ to keep it simple and familliar in this Ruby runtime API.
|
|
251
251
|
module CharacterStream
|
252
252
|
include Stream
|
253
253
|
extend ClassMacros
|
254
|
-
|
254
|
+
include Constants
|
255
|
+
#EOF = -1
|
255
256
|
|
256
257
|
##
|
257
258
|
# :method: substring(start,stop)
|
@@ -384,16 +385,16 @@ class StringStream
|
|
384
385
|
# [:line] the initial line number; default: +1+
|
385
386
|
# [:column] the initial column number; default: +0+
|
386
387
|
#
|
387
|
-
def initialize(data, options = {})
|
388
|
+
def initialize( data, options = {} )
|
388
389
|
@data = data.to_s
|
389
|
-
@data.equal?(data) and @data = @data.clone
|
390
|
+
@data.equal?( data ) and @data = @data.clone
|
390
391
|
@data.freeze
|
391
392
|
@position = 0
|
392
393
|
@line = options.fetch :line, 1
|
393
394
|
@column = options.fetch :column, 0
|
394
395
|
@markers = []
|
395
396
|
mark
|
396
|
-
@name ||= options[:file] || options[:name] # || '(string)'
|
397
|
+
@name ||= options[ :file ] || options[ :name ] # || '(string)'
|
397
398
|
end
|
398
399
|
|
399
400
|
def size
|
@@ -417,7 +418,7 @@ class StringStream
|
|
417
418
|
# advance the stream by one character; returns the character consumed
|
418
419
|
#
|
419
420
|
def consume
|
420
|
-
c = @data[@position] || EOF
|
421
|
+
c = @data[ @position ] || EOF
|
421
422
|
if @position < @data.length
|
422
423
|
@column += 1
|
423
424
|
if c == ?\n
|
@@ -426,7 +427,7 @@ class StringStream
|
|
426
427
|
end
|
427
428
|
@position += 1
|
428
429
|
end
|
429
|
-
return(c)
|
430
|
+
return( c )
|
430
431
|
end
|
431
432
|
|
432
433
|
#
|
@@ -435,25 +436,25 @@ class StringStream
|
|
435
436
|
# value of +k+ returns previous characters consumed, where <tt>k = -1</tt> is the last
|
436
437
|
# character consumed. <tt>k = 0</tt> has undefined behavior and returns +nil+
|
437
438
|
#
|
438
|
-
def peek(k = 1)
|
439
|
+
def peek( k = 1 )
|
439
440
|
k == 0 and return nil
|
440
441
|
k += 1 if k < 0
|
441
442
|
index = @position + k - 1
|
442
443
|
index < 0 and return nil
|
443
|
-
@data[index] or EOF
|
444
|
+
@data[ index ] or EOF
|
444
445
|
end
|
445
446
|
|
446
447
|
#
|
447
448
|
# identical to #peek, except it returns the character value as a String
|
448
449
|
#
|
449
|
-
def look(k = 1)
|
450
|
+
def look( k = 1 )
|
450
451
|
k == 0 and return nil
|
451
452
|
k += 1 if k < 0
|
452
453
|
|
453
454
|
index = @position + k - 1
|
454
455
|
index < 0 and return nil
|
455
456
|
|
456
|
-
c = @data[index] and c.chr
|
457
|
+
c = @data[ index ] and c.chr
|
457
458
|
end
|
458
459
|
|
459
460
|
#
|
@@ -461,9 +462,9 @@ class StringStream
|
|
461
462
|
# if <tt>k >= 0</tt>, return the next k characters
|
462
463
|
# if <tt>k < 0</tt>, return the previous <tt>|k|</tt> characters
|
463
464
|
#
|
464
|
-
def through(k)
|
465
|
+
def through( k )
|
465
466
|
if k >= 0 then @data[ @position, k ] else
|
466
|
-
start = (@position + k).at_least( 0 ) # start cannot be negative or index will wrap around
|
467
|
+
start = ( @position + k ).at_least( 0 ) # start cannot be negative or index will wrap around
|
467
468
|
@data[ start ... @position ]
|
468
469
|
end
|
469
470
|
end
|
@@ -472,7 +473,7 @@ class StringStream
|
|
472
473
|
alias >> look
|
473
474
|
|
474
475
|
# operator style look-behind
|
475
|
-
def <<(k)
|
476
|
+
def <<( k )
|
476
477
|
self << -k
|
477
478
|
end
|
478
479
|
|
@@ -486,7 +487,7 @@ class StringStream
|
|
486
487
|
# This is an extra utility method for use inside lexer actions if needed.
|
487
488
|
#
|
488
489
|
def beginning_of_line?
|
489
|
-
@position.zero? or @data[@position - 1] == ?\n
|
490
|
+
@position.zero? or @data[ @position - 1 ] == ?\n
|
490
491
|
end
|
491
492
|
|
492
493
|
#
|
@@ -494,7 +495,7 @@ class StringStream
|
|
494
495
|
# This is an extra utility method for use inside lexer actions if needed.
|
495
496
|
#
|
496
497
|
def end_of_line?
|
497
|
-
@data[@position] == ?\n if @position >= @data.length
|
498
|
+
@data[ @position ] == ?\n if @position >= @data.length
|
498
499
|
end
|
499
500
|
|
500
501
|
#
|
@@ -522,7 +523,7 @@ class StringStream
|
|
522
523
|
# position with the #rewind method. This method is used to implement backtracking.
|
523
524
|
#
|
524
525
|
def mark
|
525
|
-
state = [@position, @line, @column].freeze
|
526
|
+
state = [ @position, @line, @column ].freeze
|
526
527
|
@markers << state
|
527
528
|
return @markers.length - 1
|
528
529
|
end
|
@@ -531,10 +532,10 @@ class StringStream
|
|
531
532
|
# restore the stream to an earlier location recorded by #mark. If no marker value is
|
532
533
|
# provided, the last marker generated by #mark will be used.
|
533
534
|
#
|
534
|
-
def rewind(marker = @markers.length - 1, release = true)
|
535
|
-
(marker >= 0 and location = @markers[marker]) or return(self)
|
535
|
+
def rewind( marker = @markers.length - 1, release = true )
|
536
|
+
( marker >= 0 and location = @markers[ marker ] ) or return( self )
|
536
537
|
@position, @line, @column = location
|
537
|
-
release(marker) if release
|
538
|
+
release( marker ) if release
|
538
539
|
return self
|
539
540
|
end
|
540
541
|
|
@@ -556,9 +557,9 @@ class StringStream
|
|
556
557
|
# let go of the bookmark data for the marker and all marker
|
557
558
|
# values created after the marker.
|
558
559
|
#
|
559
|
-
def release(marker = @markers.length - 1)
|
560
|
-
marker.between?(1, @markers.length - 1) or return
|
561
|
-
@markers
|
560
|
+
def release( marker = @markers.length - 1 )
|
561
|
+
marker.between?( 1, @markers.length - 1 ) or return
|
562
|
+
@markers.pop( @markers.length - marker )
|
562
563
|
return self
|
563
564
|
end
|
564
565
|
|
@@ -567,15 +568,15 @@ class StringStream
|
|
567
568
|
# note: if +index+ is before the current position, the +line+ and +column+
|
568
569
|
# attributes of the stream will probably be incorrect
|
569
570
|
#
|
570
|
-
def seek(index)
|
571
|
+
def seek( index )
|
571
572
|
index = index.bound( 0, @data.length ) # ensures index is within the stream's range
|
572
573
|
if index > @position
|
573
574
|
skipped = through( index - @position )
|
574
|
-
if lc = skipped.count("\n") and lc.zero?
|
575
|
+
if lc = skipped.count( "\n" ) and lc.zero?
|
575
576
|
@column += skipped.length
|
576
577
|
else
|
577
578
|
@line += lc
|
578
|
-
@column = skipped.length - skipped.rindex("\n") - 1
|
579
|
+
@column = skipped.length - skipped.rindex( "\n" ) - 1
|
579
580
|
end
|
580
581
|
end
|
581
582
|
@position = index
|
@@ -589,29 +590,29 @@ class StringStream
|
|
589
590
|
# * +before_chars+ characters before the cursor (6 characters by default)
|
590
591
|
# * +after_chars+ characters after the cursor (10 characters by default)
|
591
592
|
#
|
592
|
-
def inspect(before_chars = 6, after_chars = 10)
|
593
|
+
def inspect( before_chars = 6, after_chars = 10 )
|
593
594
|
before = through( -before_chars ).inspect
|
594
|
-
@position - before_chars > 0 and before.insert(0, '... ')
|
595
|
+
@position - before_chars > 0 and before.insert( 0, '... ' )
|
595
596
|
|
596
597
|
after = through( after_chars ).inspect
|
597
598
|
@position + after_chars + 1 < @data.length and after << ' ...'
|
598
599
|
|
599
600
|
location = "#@position / line #@line:#@column"
|
600
|
-
"#<#{self.class}: #{before} | #{after} @ #{location}>"
|
601
|
+
"#<#{ self.class }: #{ before } | #{ after } @ #{ location }>"
|
601
602
|
end
|
602
603
|
|
603
604
|
#
|
604
605
|
# return the string slice between position +start+ and +stop+
|
605
606
|
#
|
606
|
-
def substring(start, stop)
|
607
|
-
@data[start, stop - start + 1]
|
607
|
+
def substring( start, stop )
|
608
|
+
@data[ start, stop - start + 1 ]
|
608
609
|
end
|
609
610
|
|
610
611
|
#
|
611
612
|
# identical to String#[]
|
612
613
|
#
|
613
|
-
def [](start, *args)
|
614
|
-
@data[start, *args]
|
614
|
+
def []( start, *args )
|
615
|
+
@data[ start, *args ]
|
615
616
|
end
|
616
617
|
end
|
617
618
|
|
@@ -640,31 +641,31 @@ class FileStream < StringStream
|
|
640
641
|
# see StringStream.new for a list of additional options
|
641
642
|
# the constructer accepts
|
642
643
|
#
|
643
|
-
def initialize(file, options = {})
|
644
|
+
def initialize( file, options = {} )
|
644
645
|
case file
|
645
646
|
when $stdin then
|
646
647
|
data = $stdin.read
|
647
648
|
@name = '(stdin)'
|
648
649
|
when ::File then
|
649
650
|
file = file.clone
|
650
|
-
file.reopen(file.path, 'r')
|
651
|
+
file.reopen( file.path, 'r' )
|
651
652
|
@name = file.path
|
652
653
|
data = file.read
|
653
654
|
file.close
|
654
655
|
else
|
655
|
-
if file.respond_to?(:read)
|
656
|
+
if file.respond_to?( :read )
|
656
657
|
data = file.read
|
657
|
-
if file.respond_to?(:name) then @name = file.name
|
658
|
-
elsif file.respond_to?(:path) then @name = file.path
|
658
|
+
if file.respond_to?( :name ) then @name = file.name
|
659
|
+
elsif file.respond_to?( :path ) then @name = file.path
|
659
660
|
end
|
660
661
|
else
|
661
662
|
@name = file.to_s
|
662
|
-
if test(?f, @name) then data = File.read(@name)
|
663
|
+
if test( ?f, @name ) then data = File.read( @name )
|
663
664
|
else raise ArgumentError, "could not find an existing file at %p" % @name
|
664
665
|
end
|
665
666
|
end
|
666
667
|
end
|
667
|
-
super(data, options)
|
668
|
+
super( data, options )
|
668
669
|
end
|
669
670
|
|
670
671
|
end
|
@@ -749,7 +750,7 @@ class CommonTokenStream
|
|
749
750
|
@tokens.each_with_index { |t, i| t.index = i }
|
750
751
|
@position =
|
751
752
|
if first_token = @tokens.find { |t| t.channel == @channel }
|
752
|
-
@tokens.index(first_token)
|
753
|
+
@tokens.index( first_token )
|
753
754
|
else @tokens.length
|
754
755
|
end
|
755
756
|
end
|
@@ -762,18 +763,18 @@ class CommonTokenStream
|
|
762
763
|
# behavior to CommonTokenStream.new, if a block is provided, tokens will be
|
763
764
|
# yielded and discarded if the block returns a +false+ or +nil+ value.
|
764
765
|
#
|
765
|
-
def rebuild(token_source = nil)
|
766
|
+
def rebuild( token_source = nil )
|
766
767
|
if token_source.nil?
|
767
768
|
@token_source.reset rescue nil
|
768
769
|
else @token_source = token_source
|
769
770
|
end
|
770
|
-
@tokens = block_given? ? @token_source.select { |token| yield(token) } :
|
771
|
+
@tokens = block_given? ? @token_source.select { |token| yield( token ) } :
|
771
772
|
@token_source.to_a
|
772
773
|
@tokens.each_with_index { |t, i| t.index = i }
|
773
774
|
@last_marker = nil
|
774
775
|
@position =
|
775
776
|
if first_token = @tokens.find { |t| t.channel == @channel }
|
776
|
-
@tokens.index(first_token)
|
777
|
+
@tokens.index( first_token )
|
777
778
|
else @tokens.length
|
778
779
|
end
|
779
780
|
return self
|
@@ -782,7 +783,7 @@ class CommonTokenStream
|
|
782
783
|
#
|
783
784
|
# tune the stream to a new channel value
|
784
785
|
#
|
785
|
-
def tune_to(channel)
|
786
|
+
def tune_to( channel )
|
786
787
|
@channel = channel
|
787
788
|
end
|
788
789
|
|
@@ -808,7 +809,7 @@ class CommonTokenStream
|
|
808
809
|
#
|
809
810
|
def reset
|
810
811
|
@position = 0
|
811
|
-
@position += 1 while token = @tokens[@position] and
|
812
|
+
@position += 1 while token = @tokens[ @position ] and
|
812
813
|
token.channel != @channel
|
813
814
|
@last_marker = nil
|
814
815
|
return self
|
@@ -821,13 +822,13 @@ class CommonTokenStream
|
|
821
822
|
@last_marker = @position
|
822
823
|
end
|
823
824
|
|
824
|
-
def release(marker = nil)
|
825
|
+
def release( marker = nil )
|
825
826
|
# do nothing
|
826
827
|
end
|
827
828
|
|
828
829
|
|
829
|
-
def rewind(marker = @last_marker, release = true)
|
830
|
-
seek(marker)
|
830
|
+
def rewind( marker = @last_marker, release = true )
|
831
|
+
seek( marker )
|
831
832
|
end
|
832
833
|
|
833
834
|
|
@@ -837,11 +838,11 @@ class CommonTokenStream
|
|
837
838
|
# advance the stream one step to the next on-channel token
|
838
839
|
#
|
839
840
|
def consume
|
840
|
-
token = @tokens[@position] || EOF_TOKEN
|
841
|
+
token = @tokens[ @position ] || EOF_TOKEN
|
841
842
|
if @position < @tokens.length
|
842
|
-
@position = future?(2) || @tokens.length
|
843
|
+
@position = future?( 2 ) || @tokens.length
|
843
844
|
end
|
844
|
-
return(token)
|
845
|
+
return( token )
|
845
846
|
end
|
846
847
|
|
847
848
|
#
|
@@ -849,8 +850,8 @@ class CommonTokenStream
|
|
849
850
|
# note: seek does not check whether or not the
|
850
851
|
# token at the specified position is on-channel,
|
851
852
|
#
|
852
|
-
def seek(index)
|
853
|
-
@position = index.to_i.bound(0, @tokens.length)
|
853
|
+
def seek( index )
|
854
|
+
@position = index.to_i.bound( 0, @tokens.length )
|
854
855
|
return self
|
855
856
|
end
|
856
857
|
|
@@ -860,16 +861,16 @@ class CommonTokenStream
|
|
860
861
|
# value of +k+ returns previous on-channel tokens consumed, where <tt>k = -1</tt> is the last
|
861
862
|
# on-channel token consumed. <tt>k = 0</tt> has undefined behavior and returns +nil+
|
862
863
|
#
|
863
|
-
def peek(k = 1)
|
864
|
-
tk = look(k) and return(tk.type)
|
864
|
+
def peek( k = 1 )
|
865
|
+
tk = look( k ) and return( tk.type )
|
865
866
|
end
|
866
867
|
|
867
868
|
#
|
868
869
|
# operates simillarly to #peek, but returns the full token object at look-ahead position +k+
|
869
870
|
#
|
870
|
-
def look(k = 1)
|
871
|
-
index = future?(k) or return nil
|
872
|
-
@tokens.fetch(index, EOF_TOKEN)
|
871
|
+
def look( k = 1 )
|
872
|
+
index = future?( k ) or return nil
|
873
|
+
@tokens.fetch( index, EOF_TOKEN )
|
873
874
|
end
|
874
875
|
|
875
876
|
alias >> look
|
@@ -881,21 +882,21 @@ class CommonTokenStream
|
|
881
882
|
# returns the index of the on-channel token at look-ahead position +k+ or nil if no other
|
882
883
|
# on-channel tokens exist
|
883
884
|
#
|
884
|
-
def future?(k = 1)
|
885
|
+
def future?( k = 1 )
|
885
886
|
@position == -1 and fill_buffer
|
886
887
|
|
887
888
|
case
|
888
889
|
when k == 0 then nil
|
889
|
-
when k < 0 then past?(-k)
|
890
|
+
when k < 0 then past?( -k )
|
890
891
|
when k == 1 then @position
|
891
892
|
else
|
892
893
|
# since the stream only yields on-channel
|
893
894
|
# tokens, the stream can't just go to the
|
894
895
|
# next position, but rather must skip
|
895
896
|
# over off-channel tokens
|
896
|
-
(k - 1).times.inject(@position) do |cursor, |
|
897
|
+
( k - 1 ).times.inject( @position ) do |cursor, |
|
897
898
|
begin
|
898
|
-
tk = @tokens.at(cursor += 1) or return(cursor)
|
899
|
+
tk = @tokens.at( cursor += 1 ) or return( cursor )
|
899
900
|
# ^- if tk is nil (i.e. i is outside array limits)
|
900
901
|
end until tk.channel == @channel
|
901
902
|
cursor
|
@@ -907,7 +908,7 @@ class CommonTokenStream
|
|
907
908
|
# returns the index of the on-channel token at look-behind position +k+ or nil if no other
|
908
909
|
# on-channel tokens exist before the current token
|
909
910
|
#
|
910
|
-
def past?(k = 1)
|
911
|
+
def past?( k = 1 )
|
911
912
|
@position == -1 and fill_buffer
|
912
913
|
|
913
914
|
case
|
@@ -915,10 +916,10 @@ class CommonTokenStream
|
|
915
916
|
when @position - k < 0 then nil
|
916
917
|
else
|
917
918
|
|
918
|
-
k.times.inject(@position) do |cursor, |
|
919
|
+
k.times.inject( @position ) do |cursor, |
|
919
920
|
begin
|
920
|
-
cursor <= 0 and return(nil)
|
921
|
-
tk = @tokens.at(cursor -= 1) or return(nil)
|
921
|
+
cursor <= 0 and return( nil )
|
922
|
+
tk = @tokens.at( cursor -= 1 ) or return( nil )
|
922
923
|
end until tk.channel == @channel
|
923
924
|
cursor
|
924
925
|
end
|
@@ -931,9 +932,9 @@ class CommonTokenStream
|
|
931
932
|
# If no block is provided, the method returns an Enumerator object.
|
932
933
|
# #each accepts the same arguments as #tokens
|
933
934
|
#
|
934
|
-
def each(*args)
|
935
|
-
block_given? or return enum_for(:each, *args)
|
936
|
-
tokens(*args).each { |token| yield(token) }
|
935
|
+
def each( *args )
|
936
|
+
block_given? or return enum_for( :each, *args )
|
937
|
+
tokens( *args ).each { |token| yield( token ) }
|
937
938
|
end
|
938
939
|
|
939
940
|
#
|
@@ -944,36 +945,36 @@ class CommonTokenStream
|
|
944
945
|
# yielded and filtered out of the return array if the block returns a +false+
|
945
946
|
# or +nil+ value.
|
946
947
|
#
|
947
|
-
def tokens(start = nil, stop = nil)
|
948
|
+
def tokens( start = nil, stop = nil )
|
948
949
|
stop.nil? || stop >= @tokens.length and stop = @tokens.length - 1
|
949
950
|
start.nil? || stop < 0 and start = 0
|
950
|
-
tokens = @tokens[start..stop]
|
951
|
+
tokens = @tokens[ start..stop ]
|
951
952
|
|
952
953
|
if block_given?
|
953
|
-
tokens.delete_if { |t| not yield(t) }
|
954
|
+
tokens.delete_if { |t| not yield( t ) }
|
954
955
|
end
|
955
956
|
|
956
957
|
return( tokens )
|
957
958
|
end
|
958
959
|
|
959
960
|
|
960
|
-
def at(i)
|
961
|
+
def at( i )
|
961
962
|
@tokens.at i
|
962
963
|
end
|
963
964
|
|
964
965
|
#
|
965
966
|
# identical to Array#[], as applied to the stream's token buffer
|
966
967
|
#
|
967
|
-
def [](i, *args)
|
968
|
-
@tokens[i, *args]
|
968
|
+
def []( i, *args )
|
969
|
+
@tokens[ i, *args ]
|
969
970
|
end
|
970
971
|
|
971
972
|
###### Standard Conversion Methods ###############################
|
972
973
|
def inspect
|
973
974
|
string = "#<%p: @token_source=%p @ %p/%p" %
|
974
|
-
[self.class, @token_source.class, @position, @tokens.length]
|
975
|
-
tk = look(-1) and string << " #{tk.inspect} <--"
|
976
|
-
tk = look( 1) and string << " --> #{tk.inspect}"
|
975
|
+
[ self.class, @token_source.class, @position, @tokens.length ]
|
976
|
+
tk = look( -1 ) and string << " #{ tk.inspect } <--"
|
977
|
+
tk = look( 1 ) and string << " --> #{ tk.inspect }"
|
977
978
|
string << '>'
|
978
979
|
end
|
979
980
|
|
@@ -981,14 +982,14 @@ class CommonTokenStream
|
|
981
982
|
# fetches the text content of all tokens between +start+ and +stop+ and
|
982
983
|
# joins the chunks into a single string
|
983
984
|
#
|
984
|
-
def extract_text(start = 0, stop = @tokens.length - 1)
|
985
|
-
start = start.to_i.at_least(0)
|
986
|
-
stop = stop.to_i.at_most(@tokens.length)
|
987
|
-
@tokens[start..stop].map! { |t| t.text }.join('')
|
985
|
+
def extract_text( start = 0, stop = @tokens.length - 1 )
|
986
|
+
start = start.to_i.at_least( 0 )
|
987
|
+
stop = stop.to_i.at_most( @tokens.length )
|
988
|
+
@tokens[ start..stop ].map! { |t| t.text }.join( '' )
|
988
989
|
end
|
989
990
|
|
990
991
|
alias to_s extract_text
|
991
992
|
|
992
993
|
end
|
993
994
|
|
994
|
-
end
|
995
|
+
end
|