1  package frost.io
 2  
 3  ====================================================================================================
 4  A `InputStream` which sits on top of another stream, tracking the current line and column as it
 5  reads from the underlying stream. Closing the `LineNumberInputStream` *does not* close the
 6  underlying stream, but if the `LineNumberInputStream` holds the only remaining reference to the
 7  underlying stream, then discarding the `LineNumberInputStream` will cause the underlying stream
 8  to likewise be discarded and thus closed.
 9  
10  You should not read directly from the underlying stream after creating a `LineNumberInputStream` on
11  top of it, as doing so will naturally throw off the `LineNumberInputStream`'s calculations.
12  ====================================================================================================
13  class LineNumberInputStream : InputStream {
14      @private
15      def source:InputStream
16  
17      ================================================================================================
18      The current line number (starting at 1).
19      ================================================================================================
20      var line := 1
21  
22      ================================================================================================
23      The current column number (starting at 1).
24      ================================================================================================
25      var column := 1
26  
27      ================================================================================================
28      The width of a tab character.
29      ================================================================================================
30      var tabSize := 4
31  
32      ================================================================================================
33      Creates a new `LineNumberInputStream`.
34  
35      @param source the underlying stream
36      ================================================================================================
37      init(source:InputStream) {
38          self.source := source
39          super.init()
40      }
41  
42      @override
43      method read():UInt8? {
44          def result:UInt8? := source.read()
45          if result == null {
46              return null
47          }
48          match result {
49              when 10 {
50                  line += 1
51                  column := 1
52              }
53              when 9 {
54                  column += tabSize - column % tabSize
55              }
56              otherwise {
57                  column += 1
58              }
59          }
60          return result
61      }
62  }