pos.mx raw
1 package main
2
3 import "fmt"
4
5 const PosMax = 1 << 30
6
7 type Pos struct {
8 base *PosBase
9 line, col uint32
10 }
11
12 func MakePos(base *PosBase, line, col uint32) Pos { return Pos{base, sat32(line), sat32(col)} }
13
14 func (pos Pos) Pos() Pos { return pos }
15 func (pos Pos) IsKnown() bool { return pos.line > 0 }
16 func (pos Pos) Base() *PosBase { return pos.base }
17 func (pos Pos) Line() uint32 { return pos.line }
18 func (pos Pos) Col() uint32 { return pos.col }
19
20 func (pos Pos) FileBase() *PosBase {
21 b := pos.base
22 for b != nil && b != b.pos.base {
23 b = b.pos.base
24 }
25 return b
26 }
27
28 func (pos Pos) RelFilename() string { return pos.base.Filename() }
29
30 func (pos Pos) RelLine() uint32 {
31 b := pos.base
32 if b.Line() == 0 {
33 return 0
34 }
35 return b.Line() + (pos.Line() - b.Pos().Line())
36 }
37
38 func (pos Pos) RelCol() uint32 {
39 b := pos.base
40 if b.Col() == 0 {
41 return 0
42 }
43 if pos.Line() == b.Pos().Line() {
44 return b.Col() + (pos.Col() - b.Pos().Col())
45 }
46 return pos.Col()
47 }
48
49 func (p Pos) Cmp(q Pos) int {
50 pname := p.RelFilename()
51 qname := q.RelFilename()
52 switch {
53 case pname < qname:
54 return -1
55 case pname > qname:
56 return +1
57 }
58
59 pline := p.Line()
60 qline := q.Line()
61 switch {
62 case pline < qline:
63 return -1
64 case pline > qline:
65 return +1
66 }
67
68 pcol := p.Col()
69 qcol := q.Col()
70 switch {
71 case pcol < qcol:
72 return -1
73 case pcol > qcol:
74 return +1
75 }
76
77 return 0
78 }
79
80 func (pos Pos) String() string {
81 rel := position_{pos.RelFilename(), pos.RelLine(), pos.RelCol()}
82 abs := position_{pos.Base().Pos().RelFilename(), pos.Line(), pos.Col()}
83 s := rel.String()
84 if rel != abs {
85 s = s | "[" | abs.String() | "]"
86 }
87 return s
88 }
89
90 type position_ struct {
91 filename string
92 line, col uint32
93 }
94
95 func (p position_) String() string {
96 if p.line == 0 {
97 if p.filename == "" {
98 return "<unknown position>"
99 }
100 return p.filename
101 }
102 if p.col == 0 {
103 return fmt.Sprintf("%s:%d", p.filename, p.line)
104 }
105 return fmt.Sprintf("%s:%d:%d", p.filename, p.line, p.col)
106 }
107
108 type PosBase struct {
109 pos Pos
110 filename string
111 line, col uint32
112 trimmed bool
113 }
114
115 func NewFileBase(filename string) *PosBase {
116 return NewTrimmedFileBase(filename, false)
117 }
118
119 func NewTrimmedFileBase(filename string, trimmed bool) *PosBase {
120 base := &PosBase{MakePos(nil, Linebase, Colbase), filename, Linebase, Colbase, trimmed}
121 base.pos.base = base
122 return base
123 }
124
125 func NewLineBase(pos Pos, filename string, trimmed bool, line, col uint32) *PosBase {
126 return &PosBase{pos, filename, sat32(line), sat32(col), trimmed}
127 }
128
129 func (base *PosBase) IsFileBase() bool {
130 if base == nil {
131 return false
132 }
133 return base.pos.base == base
134 }
135
136 func (base *PosBase) Pos() (_ Pos) {
137 if base == nil {
138 return
139 }
140 return base.pos
141 }
142
143 func (base *PosBase) Filename() string {
144 if base == nil {
145 return ""
146 }
147 return base.filename
148 }
149
150 func (base *PosBase) Line() uint32 {
151 if base == nil {
152 return 0
153 }
154 return base.line
155 }
156
157 func (base *PosBase) Col() uint32 {
158 if base == nil {
159 return 0
160 }
161 return base.col
162 }
163
164 func (base *PosBase) Trimmed() bool {
165 if base == nil {
166 return false
167 }
168 return base.trimmed
169 }
170
171 func sat32(x uint32) uint32 {
172 if x > PosMax {
173 return PosMax
174 }
175 return uint32(x)
176 }
177