diff options
Diffstat (limited to 'compiler/src/dotty/tools/dotc/util/Positions.scala')
-rw-r--r-- | compiler/src/dotty/tools/dotc/util/Positions.scala | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/util/Positions.scala b/compiler/src/dotty/tools/dotc/util/Positions.scala new file mode 100644 index 000000000..c3890cc9a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/util/Positions.scala @@ -0,0 +1,173 @@ +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) +} |