Source file src/cmd/compile/internal/syntax/pos.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package syntax
     6  
     7  import "fmt"
     8  
     9  // PosMax is the largest line or column value that can be represented without loss.
    10  // Incoming values (arguments) larger than PosMax will be set to PosMax.
    11  //
    12  // Keep this consistent with maxLineCol in go/scanner.
    13  const PosMax = 1 << 30
    14  
    15  // A Pos represents an absolute (line, col) source position
    16  // with a reference to position base for computing relative
    17  // (to a file, or line directive) position information.
    18  // Pos values are intentionally light-weight so that they
    19  // can be created without too much concern about space use.
    20  type Pos struct {
    21  	base      *PosBase
    22  	line, col uint32
    23  }
    24  
    25  // MakePos returns a new Pos for the given PosBase, line and column.
    26  func MakePos(base *PosBase, line, col uint) Pos { return Pos{base, sat32(line), sat32(col)} }
    27  
    28  // TODO(gri) IsKnown makes an assumption about linebase < 1.
    29  // Maybe we should check for Base() != nil instead.
    30  
    31  func (pos Pos) Pos() Pos       { return pos }
    32  func (pos Pos) IsKnown() bool  { return pos.line > 0 }
    33  func (pos Pos) Base() *PosBase { return pos.base }
    34  func (pos Pos) Line() uint     { return uint(pos.line) }
    35  func (pos Pos) Col() uint      { return uint(pos.col) }
    36  
    37  func (pos Pos) RelFilename() string { return pos.base.Filename() }
    38  
    39  func (pos Pos) RelLine() uint {
    40  	b := pos.base
    41  	if b.Line() == 0 {
    42  		// base line is unknown => relative line is unknown
    43  		return 0
    44  	}
    45  	return b.Line() + (pos.Line() - b.Pos().Line())
    46  }
    47  
    48  func (pos Pos) RelCol() uint {
    49  	b := pos.base
    50  	if b.Col() == 0 {
    51  		// base column is unknown => relative column is unknown
    52  		// (the current specification for line directives requires
    53  		// this to apply until the next PosBase/line directive,
    54  		// not just until the new newline)
    55  		return 0
    56  	}
    57  	if pos.Line() == b.Pos().Line() {
    58  		// pos on same line as pos base => column is relative to pos base
    59  		return b.Col() + (pos.Col() - b.Pos().Col())
    60  	}
    61  	return pos.Col()
    62  }
    63  
    64  // Cmp compares the positions p and q and returns a result r as follows:
    65  //
    66  //	r <  0: p is before q
    67  //	r == 0: p and q are the same position (but may not be identical)
    68  //	r >  0: p is after q
    69  //
    70  // If p and q are in different files, p is before q if the filename
    71  // of p sorts lexicographically before the filename of q.
    72  func (p Pos) Cmp(q Pos) int {
    73  	pname := p.RelFilename()
    74  	qname := q.RelFilename()
    75  	switch {
    76  	case pname < qname:
    77  		return -1
    78  	case pname > qname:
    79  		return +1
    80  	}
    81  
    82  	pline := p.Line()
    83  	qline := q.Line()
    84  	switch {
    85  	case pline < qline:
    86  		return -1
    87  	case pline > qline:
    88  		return +1
    89  	}
    90  
    91  	pcol := p.Col()
    92  	qcol := q.Col()
    93  	switch {
    94  	case pcol < qcol:
    95  		return -1
    96  	case pcol > qcol:
    97  		return +1
    98  	}
    99  
   100  	return 0
   101  }
   102  
   103  func (pos Pos) String() string {
   104  	rel := position_{pos.RelFilename(), pos.RelLine(), pos.RelCol()}
   105  	abs := position_{pos.Base().Pos().RelFilename(), pos.Line(), pos.Col()}
   106  	s := rel.String()
   107  	if rel != abs {
   108  		s += "[" + abs.String() + "]"
   109  	}
   110  	return s
   111  }
   112  
   113  // TODO(gri) cleanup: find better name, avoid conflict with position in error_test.go
   114  type position_ struct {
   115  	filename  string
   116  	line, col uint
   117  }
   118  
   119  func (p position_) String() string {
   120  	if p.line == 0 {
   121  		if p.filename == "" {
   122  			return "<unknown position>"
   123  		}
   124  		return p.filename
   125  	}
   126  	if p.col == 0 {
   127  		return fmt.Sprintf("%s:%d", p.filename, p.line)
   128  	}
   129  	return fmt.Sprintf("%s:%d:%d", p.filename, p.line, p.col)
   130  }
   131  
   132  // A PosBase represents the base for relative position information:
   133  // At position pos, the relative position is filename:line:col.
   134  type PosBase struct {
   135  	pos       Pos
   136  	filename  string
   137  	line, col uint32
   138  	trimmed   bool // whether -trimpath has been applied
   139  }
   140  
   141  // NewFileBase returns a new PosBase for the given filename.
   142  // A file PosBase's position is relative to itself, with the
   143  // position being filename:1:1.
   144  func NewFileBase(filename string) *PosBase {
   145  	return NewTrimmedFileBase(filename, false)
   146  }
   147  
   148  // NewTrimmedFileBase is like NewFileBase, but allows specifying Trimmed.
   149  func NewTrimmedFileBase(filename string, trimmed bool) *PosBase {
   150  	base := &PosBase{MakePos(nil, linebase, colbase), filename, linebase, colbase, trimmed}
   151  	base.pos.base = base
   152  	return base
   153  }
   154  
   155  // NewLineBase returns a new PosBase for a line directive "line filename:line:col"
   156  // relative to pos, which is the position of the character immediately following
   157  // the comment containing the line directive. For a directive in a line comment,
   158  // that position is the beginning of the next line (i.e., the newline character
   159  // belongs to the line comment).
   160  func NewLineBase(pos Pos, filename string, trimmed bool, line, col uint) *PosBase {
   161  	return &PosBase{pos, filename, sat32(line), sat32(col), trimmed}
   162  }
   163  
   164  func (base *PosBase) IsFileBase() bool {
   165  	if base == nil {
   166  		return false
   167  	}
   168  	return base.pos.base == base
   169  }
   170  
   171  func (base *PosBase) Pos() (_ Pos) {
   172  	if base == nil {
   173  		return
   174  	}
   175  	return base.pos
   176  }
   177  
   178  func (base *PosBase) Filename() string {
   179  	if base == nil {
   180  		return ""
   181  	}
   182  	return base.filename
   183  }
   184  
   185  func (base *PosBase) Line() uint {
   186  	if base == nil {
   187  		return 0
   188  	}
   189  	return uint(base.line)
   190  }
   191  
   192  func (base *PosBase) Col() uint {
   193  	if base == nil {
   194  		return 0
   195  	}
   196  	return uint(base.col)
   197  }
   198  
   199  func (base *PosBase) Trimmed() bool {
   200  	if base == nil {
   201  		return false
   202  	}
   203  	return base.trimmed
   204  }
   205  
   206  func sat32(x uint) uint32 {
   207  	if x > PosMax {
   208  		return PosMax
   209  	}
   210  	return uint32(x)
   211  }
   212  

View as plain text