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
file_name: string;
22
23
// line << 12 + column - easier to compare
24
start: int;
25
end: int;
26
27
length: int => end - start;
28
29
is_internal: bool => self == internal;
30
is_reflected: bool => self == reflected;
31
32
init(
33
file_name: string,
34
35
start_line: int,
36
start_column: int,
37
38
end_line: int,
39
end_column: int
40
)
41
is
42
self.file_name = file_name;
43
44
if
45
start_line <= 0 \/ start_column < 0 \/
46
end_line < 0 \/ end_column < 0
47
then
48
throw IndexOutOfRangeException(
49
"invalid location {start_line},{start_column}..{end_line},{end_column}"
50
);
51
fi
52
53
start = pair(start_line, start_column);
54
end = pair(end_line, end_column);
55
56
if end == 0 then
57
end = start;
58
elif end < start then
59
let t = end;
60
end = start;
61
start = t;
62
fi
63
si
64
65
init(
66
file_name: string,
67
start: int,
68
end: int
69
)
70
is
71
self.file_name = file_name;
72
self.start = start;
73
self.end = end;
74
si
75
76
start_line: int => line_of(start);
77
78
start_column: int => column_of(start);
79
80
end_line: int => line_of(end);
81
82
end_column: int => column_of(end);
83
84
=~(other: LOCATION) -> bool =>
85
if self == other then
86
true;
87
else
88
self.start == other.start /\ self.end == other.end /\ self.file_name =~ other.file_name;
89
fi;
90
91
pair(line: int, column: int) -> int static => (line << 12) | column;
92
93
line_of(line_column: int) -> int static => line_column >> 12;
94
95
column_of(line_column: int) -> int static => line_column & 0xFFF;
96
97
contains(line_column: int) -> bool =>
98
self.start <= line_column /\
99
self.end >= line_column - 1;
100
101
contains(line: int, column: int) -> bool =>
102
(self.start_line < line \/ (self.start_line == line /\ self.start_column <= column)) /\
103
(self.end_line > line \/ (self.end_line == line /\ self.end_column >= column - 1));
104
105
// Whether `other` is fully covered by `self`. Same file is
106
// required — two locations with similar (line, column) coords
107
// from different files don't contain each other. Non-strict:
108
// a location contains itself.
109
contains(other: LOCATION) -> bool =>
110
file_name =~ other.file_name /\
111
self.start <= other.start /\
112
self.end >= other.end;
113
114
// Strict containment: covers `other` and is wider on at least
115
// one side. A location does NOT strictly contain itself, and
116
// two locations with the same extent (e.g. an overload group
117
// and its resolved member recorded at the same identifier)
118
// don't strictly contain each other.
119
strictly_contains(other: LOCATION) -> bool =>
120
contains(other) /\
121
(self.start < other.start \/ self.end > other.end);
122
123
to_string() -> string =>
124
"{file_name} {start_line},{start_column}..{end_line},{end_column}";
125
126
::(with: LOCATION) -> LOCATION =>
127
let new_start = if start < with.start then start else with.start fi in
128
let new_end = if end > with.end then end else with.end fi in
129
LOCATION(file_name, new_start, new_end);
130
131
..(with: LOCATION) -> LOCATION =>
132
let new_start = if start < with.start then start else with.start fi in
133
let new_end_raw = if end > with.start then end else with.start fi in
134
let new_end =
135
if column_of(new_end_raw) > 1 then
136
pair(column_of(new_end_raw) - 1, line_of(new_end_raw))
137
else
138
new_end_raw
139
fi
140
in
141
LOCATION(file_name, new_start, new_end);
142
si
143
144
class LOCATION_CURSOR is
145
_file_name: string;
146
147
_previous_line: int;
148
_previous_column: int;
149
150
_start_line: int;
151
_start_column: int;
152
153
_current_line: int;
154
_current_column: int;
155
156
init(file_name: string) is
157
_file_name = file_name;
158
159
_previous_line = 1;
160
_previous_column = 0;
161
162
_start_line = 1;
163
_start_column = 0;
164
165
_current_line = 1;
166
_current_column = 0;
167
si
168
169
jump(line: int, column: int) is
170
_start_line = line;
171
_start_column = column;
172
173
_current_line = line;
174
_current_column = column;
175
si
176
177
save() is
178
_previous_line = _current_line;
179
_previous_column = _current_column;
180
si
181
182
restore() is
183
_current_line = _previous_line;
184
_current_column = _previous_column;
185
si
186
187
start() is
188
_start_line = _current_line;
189
_start_column = _current_column;
190
si
191
192
next_column() is
193
_current_column = _current_column + 1;
194
si
195
196
next_line() is
197
_current_column = 0;
198
_current_line = _current_line + 1;
199
si
200
201
location: LOCATION =>
202
LOCATION(
203
_file_name,
204
205
_start_line,
206
_start_column,
207
208
_current_line,
209
_current_column
210
);
211
212
character_location: LOCATION =>
213
LOCATION(
214
_file_name,
215
216
// in practice if we report a character location, we
217
// want to report the previous character because we
218
// have one character lookahead
219
_previous_line,
220
_previous_column,
221
222
_previous_line,
223
_previous_column
224
);
225
si
226
227
class INTERNAL_LOCATION_CURSOR: LOCATION_CURSOR is
228
location: LOCATION => LOCATION.internal;
229
230
init() is
231
super.init("internal");
232
si
233
si
234
si