github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/syntax/pos.go (about)

     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  const PosMax = 1 << 30
    12  
    13  // A Pos represents an absolute (line, col) source position
    14  // with a reference to position base for computing relative
    15  // (to a file, or line directive) position information.
    16  // Pos values are intentionally light-weight so that they
    17  // can be created without too much concern about space use.
    18  type Pos struct {
    19  	base      *PosBase
    20  	line, col uint32
    21  }
    22  
    23  // MakePos returns a new Pos for the given PosBase, line and column.
    24  func MakePos(base *PosBase, line, col uint) Pos { return Pos{base, sat32(line), sat32(col)} }
    25  
    26  // TODO(gri) IsKnown makes an assumption about linebase < 1.
    27  //           Maybe we should check for Base() != nil instead.
    28  
    29  func (pos Pos) IsKnown() bool  { return pos.line > 0 }
    30  func (pos Pos) Base() *PosBase { return pos.base }
    31  func (pos Pos) Line() uint     { return uint(pos.line) }
    32  func (pos Pos) Col() uint      { return uint(pos.col) }
    33  
    34  func (pos Pos) RelFilename() string { return pos.base.Filename() }
    35  
    36  func (pos Pos) RelLine() uint {
    37  	b := pos.base
    38  	if b.Line() == 0 {
    39  		// base line is unknown => relative line is unknown
    40  		return 0
    41  	}
    42  	return b.Line() + (pos.Line() - b.Pos().Line())
    43  }
    44  
    45  func (pos Pos) RelCol() uint {
    46  	b := pos.base
    47  	if b.Col() == 0 {
    48  		// base column is unknown => relative column is unknown
    49  		// (the current specification for line directives requires
    50  		// this to apply until the next PosBase/line directive,
    51  		// not just until the new newline)
    52  		return 0
    53  	}
    54  	if pos.Line() == b.Pos().Line() {
    55  		// pos on same line as pos base => column is relative to pos base
    56  		return b.Col() + (pos.Col() - b.Pos().Col())
    57  	}
    58  	return pos.Col()
    59  }
    60  
    61  func (pos Pos) String() string {
    62  	rel := position_{pos.RelFilename(), pos.RelLine(), pos.RelCol()}
    63  	abs := position_{pos.Base().Pos().RelFilename(), pos.Line(), pos.Col()}
    64  	s := rel.String()
    65  	if rel != abs {
    66  		s += "[" + abs.String() + "]"
    67  	}
    68  	return s
    69  }
    70  
    71  // TODO(gri) cleanup: find better name, avoid conflict with position in error_test.go
    72  type position_ struct {
    73  	filename  string
    74  	line, col uint
    75  }
    76  
    77  func (p position_) String() string {
    78  	if p.line == 0 {
    79  		if p.filename == "" {
    80  			return "<unknown position>"
    81  		}
    82  		return p.filename
    83  	}
    84  	if p.col == 0 {
    85  		return fmt.Sprintf("%s:%d", p.filename, p.line)
    86  	}
    87  	return fmt.Sprintf("%s:%d:%d", p.filename, p.line, p.col)
    88  }
    89  
    90  // A PosBase represents the base for relative position information:
    91  // At position pos, the relative position is filename:line:col.
    92  type PosBase struct {
    93  	pos       Pos
    94  	filename  string
    95  	line, col uint32
    96  }
    97  
    98  // NewFileBase returns a new PosBase for the given filename.
    99  // A file PosBase's position is relative to itself, with the
   100  // position being filename:1:1.
   101  func NewFileBase(filename string) *PosBase {
   102  	base := &PosBase{MakePos(nil, linebase, colbase), filename, linebase, colbase}
   103  	base.pos.base = base
   104  	return base
   105  }
   106  
   107  // NewLineBase returns a new PosBase for a line directive "line filename:line:col"
   108  // relative to pos, which is the position of the character immediately following
   109  // the comment containing the line directive. For a directive in a line comment,
   110  // that position is the beginning of the next line (i.e., the newline character
   111  // belongs to the line comment).
   112  func NewLineBase(pos Pos, filename string, line, col uint) *PosBase {
   113  	return &PosBase{pos, filename, sat32(line), sat32(col)}
   114  }
   115  
   116  func (base *PosBase) IsFileBase() bool {
   117  	if base == nil {
   118  		return false
   119  	}
   120  	return base.pos.base == base
   121  }
   122  
   123  func (base *PosBase) Pos() (_ Pos) {
   124  	if base == nil {
   125  		return
   126  	}
   127  	return base.pos
   128  }
   129  
   130  func (base *PosBase) Filename() string {
   131  	if base == nil {
   132  		return ""
   133  	}
   134  	return base.filename
   135  }
   136  
   137  func (base *PosBase) Line() uint {
   138  	if base == nil {
   139  		return 0
   140  	}
   141  	return uint(base.line)
   142  }
   143  
   144  func (base *PosBase) Col() uint {
   145  	if base == nil {
   146  		return 0
   147  	}
   148  	return uint(base.col)
   149  }
   150  
   151  func sat32(x uint) uint32 {
   152  	if x > PosMax {
   153  		return PosMax
   154  	}
   155  	return uint32(x)
   156  }