/* NSC -- new Scala compiler * Copyright 2005-2009 LAMP/EPFL * @author Martin Odersky */ // $Id: NewCharArrayReader.scala 16893 2009-01-13 13:09:22Z cunei $ package scala.tools.nsc package util import scala.tools.nsc.util.SourceFile.{LF, FF, CR, SU} abstract class CharArrayReader { self => val buf: Array[Char] def decodeUni: Boolean = true /** An error routine to call on bad unicode escapes \\uxxxx. */ protected def error(offset: Int, msg: String) /** the last read character */ var ch: Char = _ /** The offset one past the last read character */ var charOffset: Int = 0 /** The start offset of the current line */ var lineStartOffset: Int = 0 /** The start offset of the line before the current one */ var lastLineStartOffset: Int = 0 private var lastUnicodeOffset = -1 /** Is last character a unicode escape \\uxxxx? */ def isUnicodeEscape = charOffset == lastUnicodeOffset /** Advance one character */ final def nextChar() { if (charOffset >= buf.length) { ch = SU } else { val c = buf(charOffset) ch = c charOffset += 1 if (c == '\\') potentialUnicode() else if (c < ' ') potentialLineEnd() // print("`"+ch+"'") } } /** Interpret \\uxxxx escapes */ private def potentialUnicode() { def evenSlashPrefix: Boolean = { var p = charOffset - 2 while (p >= 0 && buf(p) == '\\') p -= 1 (charOffset - p) % 2 == 0 } def udigit: Int = { val d = digit2int(buf(charOffset), 16) if (d >= 0) charOffset += 1 else error(charOffset, "error in unicode escape") d } if (charOffset < buf.length && buf(charOffset) == 'u' && decodeUni && evenSlashPrefix) { do charOffset += 1 while (charOffset < buf.length && buf(charOffset) == 'u') val code = udigit << 12 | udigit << 8 | udigit << 4 | udigit lastUnicodeOffset = charOffset ch = code.toChar } } /** Handle line ends, replace CR+LF by LF */ private def potentialLineEnd() { if (ch == CR) if (charOffset < buf.length && buf(charOffset) == LF) { charOffset += 1 ch = LF } if (ch == LF || ch == FF) { lastLineStartOffset = lineStartOffset lineStartOffset = charOffset } } /** Convert a character digit to an Int according to given base, * -1 if no success */ 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 } /** A new reader that takes off at the current character position */ def lookaheadReader = new CharArrayReader { val buf = self.buf charOffset = self.charOffset ch = self.ch override def decodeUni = self.decodeUni def error(offset: Int, msg: String) = self.error(offset, msg) } }