package dotty.tools.dotc
package util
import language.implicitConversions
/** Position format in little endian:
* Start: unsigned 26 Bits (works for source files up to 64M)
* End: unsigned 26 Bits
* Point: unsigned 12 Bits relative to start
* NoPosition encoded as -1L (this is a normally invalid position
* because point would lie beyond end.
*/
object Positions {
private val StartEndBits = 26
val StartEndMask: Long = (1L << StartEndBits) - 1
private val SyntheticPointDelta = (1 << (64 - StartEndBits * 2)) - 1
/** The maximal representable offset in a position */
val MaxOffset = StartEndMask
/** Convert offset `x` to an integer by sign extending the original
* field of `StartEndBits` width.
*/
def offsetToInt(x: Int) =
x << (32 - StartEndBits) >> (32 - StartEndBits)
/** A position indicates a range between a start offset and an end offset.
* Positions can be synthetic or source-derived. A source-derived position
* has in addition a point lies somewhere between start and end. The point
* is roughly where the ^ would go if an error was diagnosed at that position.
* All quantities are encoded opaquely in a Long.
*/
class Position(val coords: Long) extends AnyVal {
/** Is this position different from NoPosition? */
def exists = this != NoPosition
/** The start of this position. */
def start: Int = {
assert(exists)
(coords & StartEndMask).toInt
}
/** The end of this position */
def end: Int = {
assert(exists)
((coords >>> StartEndBits) & StartEndMask).toInt
}
/** The point of this position, returns start for synthetic positions */
def point: Int = {
assert(exists)
val poff = pointDelta
if (poff == SyntheticPointDelta) start else start + poff
}
/** The difference between point and start in this position */
def pointDelta =
(coords >>> (StartEndBits * 2)).toInt
def orElse(that: Position) =
if (this.exists) this else that
/** The union of two positions. This is the least range that encloses
* both positions. It is always a synthetic position.
*/
def union(that: Position) =
if (!this.exists) that
else if (!that.exists) this
else Position(this.start min that.start, this.end max that.end, this.point)
/** Does the range of this position contain the one of that position? */
def contains(that: Position): Boolean =
!that.exists || exists && (start <= that.start && end >= that.end)
/** Is this position synthetic? */
def isSynthetic = pointDelta == SyntheticPointDelta
/** Is this position source-derived? */
def isSourceDerived = !isSynthetic
/** A position where all components are shifted by a given `offset`
* relative to this position.
*/
def shift(offset: Int) =
if (exists) fromOffsets(start + offset, end + offset, pointDelta)
else this
/** The zero-extent position with start and end at the point of this position */
def focus = if (exists) Position(point) else NoPosition
/** The zero-extent position with start and end at the start of this position */
def startPos = if (exists) Position(start) else NoPosition
/** The zero-extent position with start and end at the end of this position */
def endPos = if (exists) Position(end) else NoPosition
/** A copy of this position with a different start */
def withStart(start: Int) =
fromOffsets(start, this.end, if (isSynthetic) SyntheticPointDelta else this.point - start)
/** A copy of this position with a different end */
def withEnd(end: Int) = fromOffsets(this.start, end, pointDelta)
/** A copy of this position with a different point */
def withPoint(point: Int) = fromOffsets(this.start, this.end, point - this.start)
/** A synthetic copy of this position */
def toSynthetic = if (isSynthetic) this else Position(start, end)
override def toString = {
val (left, right) = if (isSynthetic) ("<", ">") else ("[", "]")
if (exists)
s"$left$start..${if (point == start) "" else s"$point.."}$end$right"
else
s"${left}no position${right}"
}
}
private def fromOffsets(start: Int, end: Int, pointDelta: Int) = {
//assert(start <= end || start == 1 && end == 0, s"$start..$end")
new Position(
(start & StartEndMask).toLong |
((end & StartEndMask).toLong << StartEndBits) |
(pointDelta.toLong << (StartEndBits * 2)))
}
/** A synthetic position with given start and end */
def Position(start: Int, end: Int): Position = {
val pos = fromOffsets(start, end, SyntheticPointDelta)
assert(pos.isSynthetic)
pos
}
/** A source-derived position with given start, end, and point delta */
def Position(start: Int, end: Int, point: Int): Position = {
val pointDelta = (point - start) max 0
val pos = fromOffsets(start, end, if (pointDelta >= SyntheticPointDelta) 0 else pointDelta)
assert(pos.isSourceDerived)
pos
}
/** A synthetic zero-extent position that starts and ends at given `start`. */
def Position(start: Int): Position = Position(start, start)
/** A sentinel for a non-existing position */
val NoPosition = Position(1, 0)
/** The coordinate of a symbol. This is either an index or
* a zero-range position.
*/
class Coord(val encoding: Int) extends AnyVal {
def isIndex = encoding > 0
def isPosition = encoding <= 0
def toIndex: Int = {
assert(isIndex)
encoding - 1
}
def toPosition = {
assert(isPosition)
if (this == NoCoord) NoPosition else Position(-1 - encoding)
}
}
/** An index coordinate */
implicit def indexCoord(n: Int): Coord = new Coord(n + 1)
implicit def positionCoord(pos: Position): Coord =
if (pos.exists) new Coord(-(pos.point + 1))
else NoCoord
/** A sentinel for a missing coordinate */
val NoCoord = new Coord(0)
}