1#!/usr/bin/ruby
2# encoding: utf-8
3
4module ANTLR3
5
6=begin rdoc ANTLR3::Main::InteractiveStringStream
7
8A special stream used in the <b>interactive mode</b> of the Main scripts. It
9uses Readline (if available) or standard IO#gets to fetch data on demand.
10
11=end
12
13class InteractiveStringStream < StringStream
14
15  if RUBY_VERSION =~ /^1\.9/
16
17    # creates a new StringStream object where +data+ is the string data to stream.
18    # accepts the following options in a symbol-to-value hash:
19    #
20    # [:file or :name] the (file) name to associate with the stream; default: <tt>'(string)'</tt>
21    # [:line] the initial line number; default: +1+
22    # [:column] the initial column number; default: +0+
23    #
24    def initialize( options = {}, &block )      # for 1.9
25      @string = ''
26      @data   = []
27      @position = options.fetch :position, 0
28      @line     = options.fetch :line, 1
29      @column   = options.fetch :column, 0
30      @markers  = []
31      mark
32      @initialized = @eof = false
33      @readline = block or raise( ArgumentError, "no line-reading block was provided" )
34      @name ||= options[ :file ] || options[ :name ] || '(interactive)'
35    end
36
37    def readline
38      @initialized = true
39      unless @eof
40        if line = @readline.call
41          line = line.to_s.encode( Encoding::UTF_8 )
42          @string << line
43          @data.concat( line.codepoints.to_a )
44          return true
45        else
46          @eof = true
47          return false
48        end
49      end
50    end
51
52  else
53
54    # creates a new StringStream object where +data+ is the string data to stream.
55    # accepts the following options in a symbol-to-value hash:
56    #
57    # [:file or :name] the (file) name to associate with the stream; default: <tt>'(string)'</tt>
58    # [:line] the initial line number; default: +1+
59    # [:column] the initial column number; default: +0+
60    #
61    def initialize( options = {}, &block )
62      @string = @data = ''
63      @position = options.fetch :position, 0
64      @line     = options.fetch :line, 1
65      @column   = options.fetch :column, 0
66      @markers = []
67      mark
68      @initialized = @eof = false
69      @readline = block or raise( ArgumentError, "no line-reading block was provided" )
70      @name ||= options[ :file ] || options[ :name ] || '(interactive)'
71    end
72
73    def readline
74      @initialized = true
75      unless @eof
76        if line = @readline.call
77          @data << line.to_s
78          return true
79        else
80          @eof = true
81          return false
82        end
83      end
84    end
85
86  end
87
88  private :readline
89
90  def consume
91    @position < @data.size and return( super )
92    unless @eof
93      readline
94      consume
95    end
96  end
97
98  def peek( i = 1 )
99    i.zero? and return 0
100    i += 1 if i < 0
101    index = @position + i - 1
102    index < 0 and return 0
103
104    if index < @data.size
105      char = @data[ index ]
106    elsif readline
107      peek( i )
108    else EOF
109    end
110  end
111
112  def look( i = 1 )
113    peek( i ).chr rescue EOF
114  end
115
116  def substring( start, stop )
117    fill_through( stop )
118    @string[ start .. stop ]
119  end
120
121private
122
123  def fill_through( position )
124    @eof and return
125    if @position < 0 then fill_out
126    else readline until ( @data.size > @position or @eof )
127    end
128  end
129
130  def fill_out
131    @eof and return
132    readline until @eof
133  end
134end
135
136end
137