github.com/bir3/gocompiler@v0.9.2202/src/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 // 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 }