github.com/bir3/gocompiler@v0.3.205/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 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) Pos() Pos { return pos } 30 func (pos Pos) IsKnown() bool { return pos.line > 0 } 31 func (pos Pos) Base() *PosBase { return pos.base } 32 func (pos Pos) Line() uint { return uint(pos.line) } 33 func (pos Pos) Col() uint { return uint(pos.col) } 34 35 func (pos Pos) RelFilename() string { return pos.base.Filename() } 36 37 func (pos Pos) RelLine() uint { 38 b := pos.base 39 if b.Line() == 0 { 40 // base line is unknown => relative line is unknown 41 return 0 42 } 43 return b.Line() + (pos.Line() - b.Pos().Line()) 44 } 45 46 func (pos Pos) RelCol() uint { 47 b := pos.base 48 if b.Col() == 0 { 49 // base column is unknown => relative column is unknown 50 // (the current specification for line directives requires 51 // this to apply until the next PosBase/line directive, 52 // not just until the new newline) 53 return 0 54 } 55 if pos.Line() == b.Pos().Line() { 56 // pos on same line as pos base => column is relative to pos base 57 return b.Col() + (pos.Col() - b.Pos().Col()) 58 } 59 return pos.Col() 60 } 61 62 // Cmp compares the positions p and q and returns a result r as follows: 63 // 64 // r < 0: p is before q 65 // r == 0: p and q are the same position (but may not be identical) 66 // r > 0: p is after q 67 // 68 // If p and q are in different files, p is before q if the filename 69 // of p sorts lexicographically before the filename of q. 70 func (p Pos) Cmp(q Pos) int { 71 pname := p.RelFilename() 72 qname := q.RelFilename() 73 switch { 74 case pname < qname: 75 return -1 76 case pname > qname: 77 return +1 78 } 79 80 pline := p.Line() 81 qline := q.Line() 82 switch { 83 case pline < qline: 84 return -1 85 case pline > qline: 86 return +1 87 } 88 89 pcol := p.Col() 90 qcol := q.Col() 91 switch { 92 case pcol < qcol: 93 return -1 94 case pcol > qcol: 95 return +1 96 } 97 98 return 0 99 } 100 101 func (pos Pos) String() string { 102 rel := position_{pos.RelFilename(), pos.RelLine(), pos.RelCol()} 103 abs := position_{pos.Base().Pos().RelFilename(), pos.Line(), pos.Col()} 104 s := rel.String() 105 if rel != abs { 106 s += "[" + abs.String() + "]" 107 } 108 return s 109 } 110 111 // TODO(gri) cleanup: find better name, avoid conflict with position in error_test.go 112 type position_ struct { 113 filename string 114 line, col uint 115 } 116 117 func (p position_) String() string { 118 if p.line == 0 { 119 if p.filename == "" { 120 return "<unknown position>" 121 } 122 return p.filename 123 } 124 if p.col == 0 { 125 return fmt.Sprintf("%s:%d", p.filename, p.line) 126 } 127 return fmt.Sprintf("%s:%d:%d", p.filename, p.line, p.col) 128 } 129 130 // A PosBase represents the base for relative position information: 131 // At position pos, the relative position is filename:line:col. 132 type PosBase struct { 133 pos Pos 134 filename string 135 line, col uint32 136 trimmed bool // whether -trimpath has been applied 137 } 138 139 // NewFileBase returns a new PosBase for the given filename. 140 // A file PosBase's position is relative to itself, with the 141 // position being filename:1:1. 142 func NewFileBase(filename string) *PosBase { 143 return NewTrimmedFileBase(filename, false) 144 } 145 146 // NewTrimmedFileBase is like NewFileBase, but allows specifying Trimmed. 147 func NewTrimmedFileBase(filename string, trimmed bool) *PosBase { 148 base := &PosBase{MakePos(nil, linebase, colbase), filename, linebase, colbase, trimmed} 149 base.pos.base = base 150 return base 151 } 152 153 // NewLineBase returns a new PosBase for a line directive "line filename:line:col" 154 // relative to pos, which is the position of the character immediately following 155 // the comment containing the line directive. For a directive in a line comment, 156 // that position is the beginning of the next line (i.e., the newline character 157 // belongs to the line comment). 158 func NewLineBase(pos Pos, filename string, trimmed bool, line, col uint) *PosBase { 159 return &PosBase{pos, filename, sat32(line), sat32(col), trimmed} 160 } 161 162 func (base *PosBase) IsFileBase() bool { 163 if base == nil { 164 return false 165 } 166 return base.pos.base == base 167 } 168 169 func (base *PosBase) Pos() (_ Pos) { 170 if base == nil { 171 return 172 } 173 return base.pos 174 } 175 176 func (base *PosBase) Filename() string { 177 if base == nil { 178 return "" 179 } 180 return base.filename 181 } 182 183 func (base *PosBase) Line() uint { 184 if base == nil { 185 return 0 186 } 187 return uint(base.line) 188 } 189 190 func (base *PosBase) Col() uint { 191 if base == nil { 192 return 0 193 } 194 return uint(base.col) 195 } 196 197 func (base *PosBase) Trimmed() bool { 198 if base == nil { 199 return false 200 } 201 return base.trimmed 202 } 203 204 func sat32(x uint) uint32 { 205 if x > PosMax { 206 return PosMax 207 } 208 return uint32(x) 209 }