summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/syntax/pos.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/syntax/pos.go')
-rw-r--r--src/cmd/compile/internal/syntax/pos.go211
1 files changed, 211 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/syntax/pos.go b/src/cmd/compile/internal/syntax/pos.go
new file mode 100644
index 0000000..dd25d4f
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/pos.go
@@ -0,0 +1,211 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import "fmt"
+
+// PosMax is the largest line or column value that can be represented without loss.
+// Incoming values (arguments) larger than PosMax will be set to PosMax.
+//
+// Keep this consistent with maxLineCol in go/scanner.
+const PosMax = 1 << 30
+
+// A Pos represents an absolute (line, col) source position
+// with a reference to position base for computing relative
+// (to a file, or line directive) position information.
+// Pos values are intentionally light-weight so that they
+// can be created without too much concern about space use.
+type Pos struct {
+ base *PosBase
+ line, col uint32
+}
+
+// MakePos returns a new Pos for the given PosBase, line and column.
+func MakePos(base *PosBase, line, col uint) Pos { return Pos{base, sat32(line), sat32(col)} }
+
+// TODO(gri) IsKnown makes an assumption about linebase < 1.
+// Maybe we should check for Base() != nil instead.
+
+func (pos Pos) Pos() Pos { return pos }
+func (pos Pos) IsKnown() bool { return pos.line > 0 }
+func (pos Pos) Base() *PosBase { return pos.base }
+func (pos Pos) Line() uint { return uint(pos.line) }
+func (pos Pos) Col() uint { return uint(pos.col) }
+
+func (pos Pos) RelFilename() string { return pos.base.Filename() }
+
+func (pos Pos) RelLine() uint {
+ b := pos.base
+ if b.Line() == 0 {
+ // base line is unknown => relative line is unknown
+ return 0
+ }
+ return b.Line() + (pos.Line() - b.Pos().Line())
+}
+
+func (pos Pos) RelCol() uint {
+ b := pos.base
+ if b.Col() == 0 {
+ // base column is unknown => relative column is unknown
+ // (the current specification for line directives requires
+ // this to apply until the next PosBase/line directive,
+ // not just until the new newline)
+ return 0
+ }
+ if pos.Line() == b.Pos().Line() {
+ // pos on same line as pos base => column is relative to pos base
+ return b.Col() + (pos.Col() - b.Pos().Col())
+ }
+ return pos.Col()
+}
+
+// Cmp compares the positions p and q and returns a result r as follows:
+//
+// r < 0: p is before q
+// r == 0: p and q are the same position (but may not be identical)
+// r > 0: p is after q
+//
+// If p and q are in different files, p is before q if the filename
+// of p sorts lexicographically before the filename of q.
+func (p Pos) Cmp(q Pos) int {
+ pname := p.RelFilename()
+ qname := q.RelFilename()
+ switch {
+ case pname < qname:
+ return -1
+ case pname > qname:
+ return +1
+ }
+
+ pline := p.Line()
+ qline := q.Line()
+ switch {
+ case pline < qline:
+ return -1
+ case pline > qline:
+ return +1
+ }
+
+ pcol := p.Col()
+ qcol := q.Col()
+ switch {
+ case pcol < qcol:
+ return -1
+ case pcol > qcol:
+ return +1
+ }
+
+ return 0
+}
+
+func (pos Pos) String() string {
+ rel := position_{pos.RelFilename(), pos.RelLine(), pos.RelCol()}
+ abs := position_{pos.Base().Pos().RelFilename(), pos.Line(), pos.Col()}
+ s := rel.String()
+ if rel != abs {
+ s += "[" + abs.String() + "]"
+ }
+ return s
+}
+
+// TODO(gri) cleanup: find better name, avoid conflict with position in error_test.go
+type position_ struct {
+ filename string
+ line, col uint
+}
+
+func (p position_) String() string {
+ if p.line == 0 {
+ if p.filename == "" {
+ return "<unknown position>"
+ }
+ return p.filename
+ }
+ if p.col == 0 {
+ return fmt.Sprintf("%s:%d", p.filename, p.line)
+ }
+ return fmt.Sprintf("%s:%d:%d", p.filename, p.line, p.col)
+}
+
+// A PosBase represents the base for relative position information:
+// At position pos, the relative position is filename:line:col.
+type PosBase struct {
+ pos Pos
+ filename string
+ line, col uint32
+ trimmed bool // whether -trimpath has been applied
+}
+
+// NewFileBase returns a new PosBase for the given filename.
+// A file PosBase's position is relative to itself, with the
+// position being filename:1:1.
+func NewFileBase(filename string) *PosBase {
+ return NewTrimmedFileBase(filename, false)
+}
+
+// NewTrimmedFileBase is like NewFileBase, but allows specifying Trimmed.
+func NewTrimmedFileBase(filename string, trimmed bool) *PosBase {
+ base := &PosBase{MakePos(nil, linebase, colbase), filename, linebase, colbase, trimmed}
+ base.pos.base = base
+ return base
+}
+
+// NewLineBase returns a new PosBase for a line directive "line filename:line:col"
+// relative to pos, which is the position of the character immediately following
+// the comment containing the line directive. For a directive in a line comment,
+// that position is the beginning of the next line (i.e., the newline character
+// belongs to the line comment).
+func NewLineBase(pos Pos, filename string, trimmed bool, line, col uint) *PosBase {
+ return &PosBase{pos, filename, sat32(line), sat32(col), trimmed}
+}
+
+func (base *PosBase) IsFileBase() bool {
+ if base == nil {
+ return false
+ }
+ return base.pos.base == base
+}
+
+func (base *PosBase) Pos() (_ Pos) {
+ if base == nil {
+ return
+ }
+ return base.pos
+}
+
+func (base *PosBase) Filename() string {
+ if base == nil {
+ return ""
+ }
+ return base.filename
+}
+
+func (base *PosBase) Line() uint {
+ if base == nil {
+ return 0
+ }
+ return uint(base.line)
+}
+
+func (base *PosBase) Col() uint {
+ if base == nil {
+ return 0
+ }
+ return uint(base.col)
+}
+
+func (base *PosBase) Trimmed() bool {
+ if base == nil {
+ return false
+ }
+ return base.trimmed
+}
+
+func sat32(x uint) uint32 {
+ if x > PosMax {
+ return PosMax
+ }
+ return uint32(x)
+}