summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/util/CharArrayReader.scala
blob: 9a0659526a492854a73461348736ac192d7fc1ab (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* NSC -- new Scala compiler
 * Copyright 2005-2007 LAMP/EPFL
 * @author  Martin Odersky
 */
// $Id$

package scala.tools.nsc.util

import scala.tools.nsc.util.SourceFile.{LF, FF, CR, SU}

class CharArrayReader(buf: RandomAccessSeq[Char], start: Int, /* startline: int, startcol: int, */
                      decodeUni: Boolean, error: String => Unit) extends Iterator[Char] with Cloneable {

  def this(buf: RandomAccessSeq[Char], decodeUni: Boolean, error: String => Unit) =
    this(buf, 0, /* 1, 1, */ decodeUni, error)

  /** produce a duplicate of this char array reader which starts reading
    *  at current position, independent of what happens to original reader
	*/
  def dup: CharArrayReader = clone().asInstanceOf[CharArrayReader]

  /** layout constant
   */
  val tabinc = 8

  /** the line and column position of the current character
  */
  var ch: Char = _
  var bp = start
  var oldBp = -1
  var oldCh: Char = _

  //private var cline: Int = _
  //private var ccol: Int = _
  def cpos = bp
  var isUnicode: Boolean = _
  var lastLineStartPos: Int = 0
  var lineStartPos: Int = 0
  var lastBlankLinePos: Int = 0

  private var onlyBlankChars = false
  //private var nextline = startline
  //private var nextcol = startcol

  private def markNewLine() {
    lastLineStartPos = lineStartPos
    if (onlyBlankChars) lastBlankLinePos = lineStartPos
    lineStartPos = bp
    onlyBlankChars = true
    //nextline += 1
    //nextcol = 1
  }

  def hasNext: Boolean = if (bp < buf.length) true
  else {
    false
  }

  def last: Char = if (bp > start + 2) buf(bp - 2) else ' ' // XML literals

  def next: Char = {
    //cline = nextline
    //ccol = nextcol
    val buf = this.buf.asInstanceOf[runtime.BoxedCharArray].value
    if(!hasNext) {
      ch = SU
      return SU  // there is an endless stream of SU's at the end
    }
    oldBp = bp
    oldCh = ch
    ch = buf(bp)
    isUnicode = false
    bp = bp + 1
    ch match {
      case '\t' =>
        // nextcol = ((nextcol - 1) / tabinc * tabinc) + tabinc + 1;
      case CR =>
        if (buf(bp) == LF) {
          ch = LF
          bp += 1
        }
        markNewLine()
      case LF | FF =>
        markNewLine()
      case '\\' =>
        def evenSlashPrefix: Boolean = {
          var p = bp - 2
          while (p >= 0 && buf(p) == '\\') p -= 1
          (bp - p) % 2 == 0
        }
        def udigit: Int = {
          val d = digit2int(buf(bp), 16)
          if (d >= 0) { bp += 1; /* nextcol = nextcol + 1 */ }
          else error("error in unicode escape");
          d
        }
        // nextcol += 1
        if (buf(bp) == 'u' && decodeUni && evenSlashPrefix) {
          do {
            bp += 1 //; nextcol += 1
          } while (buf(bp) == 'u');
          val code = udigit << 12 | udigit << 8 | udigit << 4 | udigit
          ch = code.asInstanceOf[Char]
          isUnicode = true
        }
      case _ =>
        if (ch > ' ') onlyBlankChars = false
        // nextcol += 1
    }
    ch
  }

  def rewind {
    if (oldBp == -1) throw new IllegalArgumentException
    bp = oldBp
    ch = oldCh
    oldBp = -1
    oldCh = 'x'
  }

  def copy: CharArrayReader =
    new CharArrayReader(buf, bp, /* nextcol, nextline, */ decodeUni, error)

  def digit2int(ch: Char, base: Int): Int = {
    if ('0' <= ch && ch <= '9' && ch < '0' + base)
      ch - '0'
    else if ('A' <= ch && ch < 'A' + base - 10)
      ch - 'A' + 10
    else if ('a' <= ch && ch < 'a' + base - 10)
      ch - 'a' + 10
    else
      -1
  }
}