diff options
Diffstat (limited to 'src/cmd/compile/internal/syntax/pos.go')
-rw-r--r-- | src/cmd/compile/internal/syntax/pos.go | 211 |
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) +} |