Skip to content
← Back

src/source/location.ghul

1
namespace Source is
2
use System.IndexOutOfRangeException
3
4
use Logging
5
6
class LOCATION is
7
_internal: LOCATION static
8
_unknown: LOCATION static
9
_reflected: LOCATION static
10
11
internal: LOCATION static => _internal
12
unknown: LOCATION static => _unknown
13
reflected: LOCATION static => _reflected
14
15
init_static() static is
16
_internal = LOCATION("internal", 1, 1, 1, 1)
17
_unknown = LOCATION("unknown", 1, 1, 1, 1)
18
_reflected = LOCATION("reflected", 1, 1, 1, 1)
19
si
20
21
// A location spanning the whole of `file_name`, for a diagnostic
22
// that applies to the file rather than to any one position in it
23
// (a file-level `@@suppress`). The end is the widest position the
24
// line/column packing can represent - not a real position in the
25
// file - so this contains any location `contains`/`strictly_contains`
26
// would compare it against, however long the file actually is.
27
whole_file(file_name: string) -> LOCATION static =>
28
LOCATION(file_name, 1, 1, _MAX_LINE, _MAX_COLUMN)
29
30
// The largest line and column pair() can hold without a real
31
// position ever reaching it: the encoding gives 12 bits to the
32
// column (0xFFF, matching column_of's mask), and a line count this
33
// large stays clear of the top bits pair() shifts it into.
34
_MAX_LINE: int static => 500000
35
_MAX_COLUMN: int static => 0xFFF
36
37
file_name: string
38
39
// line << 12 + column - easier to compare
40
start: int
41
end: int
42
43
length: int => end - start
44
45
is_internal: bool => self == internal
46
is_reflected: bool => self == reflected
47
is_unknown: bool => self == unknown
48
49
init(
50
file_name: string,
51
52
start_line: int,
53
start_column: int,
54
55
end_line: int,
56
end_column: int
57
)
58
is
59
self.file_name = file_name
60
61
if
62
start_line <= 0 \/ start_column < 0 \/
63
end_line < 0 \/ end_column < 0
64
then
65
throw IndexOutOfRangeException(
66
"invalid location {start_line},{start_column}..{end_line},{end_column}"
67
)
68
fi
69
70
start = pair(start_line, start_column)
71
end = pair(end_line, end_column)
72
73
if end == 0 then
74
end = start
75
elif end < start then
76
let t = end
77
end = start
78
start = t
79
fi
80
si
81
82
init(
83
file_name: string,
84
start: int,
85
end: int
86
)
87
is
88
self.file_name = file_name
89
self.start = start
90
self.end = end
91
si
92
93
start_line: int => line_of(start)
94
95
start_column: int => column_of(start)
96
97
end_line: int => line_of(end)
98
99
end_column: int => column_of(end)
100
101
=~(other: LOCATION) -> bool =>
102
if self == other then
103
true
104
else
105
self.start == other.start /\ self.end == other.end /\ self.file_name =~ other.file_name
106
fi
107
108
get_hash_code() -> int => start ^ (end << 1) ^ file_name.get_hash_code()
109
110
pair(line: int, column: int) -> int static => (line << 12) | column
111
112
line_of(line_column: int) -> int static => line_column >> 12
113
114
column_of(line_column: int) -> int static => line_column & 0xFFF
115
116
contains(line_column: int) -> bool =>
117
self.start <= line_column /\
118
self.end >= line_column - 1
119
120
contains(line: int, column: int) -> bool =>
121
(self.start_line < line \/ (self.start_line == line /\ self.start_column <= column)) /\
122
(self.end_line > line \/ (self.end_line == line /\ self.end_column >= column - 1))
123
124
// Whether `other` is fully covered by `self`. Same file is
125
// required — two locations with similar (line, column) coords
126
// from different files don't contain each other. Non-strict:
127
// a location contains itself.
128
contains(other: LOCATION) -> bool =>
129
file_name =~ other.file_name /\
130
self.start <= other.start /\
131
self.end >= other.end
132
133
// Strict containment: covers `other` and is wider on at least
134
// one side. A location does NOT strictly contain itself, and
135
// two locations with the same extent (e.g. an overload group
136
// and its resolved member recorded at the same identifier)
137
// don't strictly contain each other.
138
strictly_contains(other: LOCATION) -> bool =>
139
contains(other) /\
140
(self.start < other.start \/ self.end > other.end)
141
142
// The zero-width position at this location's start. Combined
143
// with `::` it builds a span running from here to the end of
144
// some inner location.
145
start_position: LOCATION => LOCATION(file_name, start, start)
146
147
to_string() -> string =>
148
"{file_name} {start_line},{start_column}..{end_line},{end_column}"
149
150
::(with: LOCATION) -> LOCATION =>
151
let new_start = if start < with.start then start else with.start fi in
152
let new_end = if end > with.end then end else with.end fi in
153
LOCATION(file_name, new_start, new_end)
154
155
..(with: LOCATION) -> LOCATION =>
156
let new_start = if start < with.start then start else with.start fi in
157
let new_end_raw = if end > with.start then end else with.start fi in
158
let new_end =
159
if column_of(new_end_raw) > 1 then
160
pair(column_of(new_end_raw) - 1, line_of(new_end_raw))
161
else
162
new_end_raw
163
fi
164
in
165
LOCATION(file_name, new_start, new_end)
166
si
167
168
class LOCATION_CURSOR is
169
_file_name: string
170
171
_previous_line: int
172
_previous_column: int
173
174
_start_line: int
175
_start_column: int
176
177
_current_line: int
178
_current_column: int
179
180
init(file_name: string) is
181
_file_name = file_name
182
183
_previous_line = 1
184
_previous_column = 0
185
186
_start_line = 1
187
_start_column = 0
188
189
_current_line = 1
190
_current_column = 0
191
si
192
193
jump(line: int, column: int) is
194
_start_line = line
195
_start_column = column
196
197
_current_line = line
198
_current_column = column
199
si
200
201
save() is
202
_previous_line = _current_line
203
_previous_column = _current_column
204
si
205
206
restore() is
207
_current_line = _previous_line
208
_current_column = _previous_column
209
si
210
211
start() is
212
_start_line = _current_line
213
_start_column = _current_column
214
si
215
216
next_column() is
217
_current_column = _current_column + 1
218
si
219
220
next_line() is
221
_current_column = 0
222
_current_line = _current_line + 1
223
si
224
225
location: LOCATION =>
226
LOCATION(
227
_file_name,
228
229
_start_line,
230
_start_column,
231
232
_current_line,
233
_current_column
234
)
235
236
character_location: LOCATION =>
237
LOCATION(
238
_file_name,
239
240
// in practice if we report a character location, we
241
// want to report the previous character because we
242
// have one character lookahead
243
_previous_line,
244
_previous_column,
245
246
_previous_line,
247
_previous_column
248
)
249
si
250
251
class INTERNAL_LOCATION_CURSOR: LOCATION_CURSOR is
252
location: LOCATION => LOCATION.internal
253
254
init() is
255
super.init("internal")
256
si
257
si
258
si