1 module source.location;
2 
3 import source.context;
4 
5 /**
6  * Struct representing a location in a source file.
7  * Effectively a pair of Position within the source file.
8  */
9 struct Location {
10 package:
11 	Position _start;
12 	Position _stop;
13 	
14 public:
15 	this(Position start, Position stop) in {
16 		assert(start.isMixin() == stop.isMixin());
17 		assert(start.offset <= stop.offset);
18 	} do {
19 		this._start = start;
20 		this._stop = stop;
21 	}
22 	
23 	@property
24 	Position start() const {
25 		return _start;
26 	}
27 	
28 	@property
29 	Position stop() const {
30 		return _stop;
31 	}
32 	
33 	@property
34 	uint length() const {
35 		return stop.offset - start.offset;
36 	}
37 	
38 	@property
39 	bool isFile() const {
40 		return start.isFile();
41 	}
42 	
43 	@property
44 	bool isMixin() const {
45 		return start.isMixin();
46 	}
47 	
48 	void spanTo(Location end) in {
49 		import std.conv;
50 		assert(
51 			stop.offset <= end.stop.offset,
52 			to!string(stop.offset) ~ " > " ~ to!string(end.stop.offset)
53 		);
54 	} do {
55 		spanTo(end.stop);
56 	}
57 	
58 	void spanTo(Position end) in {
59 		import std.conv;
60 		assert(
61 			stop.offset <= end.offset,
62 			to!string(stop.offset) ~ " > " ~ to!string(end.offset)
63 		);
64 	} do {
65 		_stop = end;
66 	}
67 	
68 	auto getFullLocation(Context c) const {
69 		return FullLocation(this, c);
70 	}
71 }
72 
73 /**
74  * Struct representing a position in a source file.
75  */
76 struct Position {
77 private:
78 	import std.bitmanip;
79 	mixin(bitfields!(
80 		uint, "_offset", uint.sizeof * 8 - 1,
81 		bool, "_mixin", 1,
82 	));
83 	
84 package:
85 	@property
86 	uint offset() const {
87 		return _offset;
88 	}
89 	
90 	@property
91 	uint raw() const {
92 		return *(cast(uint*) &this);
93 	}
94 	
95 	bool isFile() const {
96 		return !_mixin;
97 	}
98 	
99 	bool isMixin() const {
100 		return _mixin;
101 	}
102 	
103 public:
104 	Position getWithOffset(uint offset) const out(result) {
105 		assert(result.isMixin() == isMixin(), "Position overflow");
106 	} do {
107 		return Position(raw + offset);
108 	}
109 	
110 	Location getWithOffsets(uint start, uint stop) {
111 		return Location(getWithOffset(start), getWithOffset(stop));
112 	}
113 	
114 	auto getFullPosition(Context c) const {
115 		return FullPosition(this, c);
116 	}
117 }
118 
119 /**
120  * A Location associated with a context, so it can probe various infos.
121  */
122 struct FullLocation {
123 private:
124 	Location _location;
125 	Context context;
126 	
127 	@property
128 	inout(FullPosition) start() inout {
129 		return inout(FullPosition)(location.start, context);
130 	}
131 	
132 	@property
133 	inout(FullPosition) stop() inout {
134 		return inout(FullPosition)(location.stop, context);
135 	}
136 	
137 	@property
138 	ref sourceManager() inout {
139 		return context.sourceManager;
140 	}
141 	
142 public:
143 	this(Location location, Context context) {
144 		this._location = location;
145 		this.context = context;
146 		
147 		import std.conv;
148 		assert(
149 			length == 0 ||
150 			start.getSource() == Position(stop.raw - 1).getFullPosition(context).getSource(),
151 /+
152 			"Location file mismatch " ~
153 				start.getFileName() ~ ":" ~ to!string(getStartOffset()) ~ " and " ~
154 				stop.getFileName() ~ ":" ~ to!string(getStopOffset())
155 /* +/ /*/ /+ */
156 			"Location file mismatch"
157 // +/
158 		);
159 	}
160 	
161 	alias location this;
162 	@property location() const {
163 		return _location;
164 	}
165 	
166 	auto getSource() out(result) {
167 		assert(result.isMixin() == isMixin());
168 	} do {
169 		return start.getSource();
170 	}
171 	
172 	string getSlice() {
173 		return getSource().getSlice(this);
174 	}
175 	
176 	uint getStartLineNumber() {
177 		return start.getLineNumber();
178 	}
179 	
180 	uint getStopLineNumber() {
181 		return stop.getLineNumber();
182 	}
183 	
184 	uint getStartColumn() {
185 		return start.getColumn();
186 	}
187 	
188 	uint getStopColumn() {
189 		return stop.getColumn();
190 	}
191 	
192 	uint getStartOffset() {
193 		return start.getSourceOffset();
194 	}
195 	
196 	uint getStopOffset() {
197 		return stop.getSourceOffset();
198 	}
199 }
200 
201 /**
202  * A Position associated with a context, so it can probe various infos.
203  */
204 struct FullPosition {
205 private:
206 	Position _position;
207 	Context context;
208 	
209 	@property
210 	uint offset() const {
211 		return position.offset;
212 	}
213 	
214 	@property
215 	ref sourceManager() inout {
216 		return context.sourceManager;
217 	}
218 	
219 public:
220 	alias position this;
221 	@property position() const {
222 		return _position;
223 	}
224 	
225 	auto getSource() out(result) {
226 		assert(result.isMixin() == isMixin());
227 	} do {
228 		return sourceManager.getFileID(this).getSource(context);
229 	}
230 	
231 	uint getLineNumber() {
232 		return sourceManager.getLineNumber(this);
233 	}
234 	
235 	uint getColumn() {
236 		return sourceManager.getColumn(this);
237 	}
238 	
239 	uint getSourceOffset() {
240 		return getSource().getOffset(this);
241 	}
242 }