FLANG

provenance.h
1 //===-- include/flang/Parser/provenance.h -----------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef FORTRAN_PARSER_PROVENANCE_H_
10 #define FORTRAN_PARSER_PROVENANCE_H_
11
12 #include "char-block.h"
13 #include "char-buffer.h"
14 #include "characters.h"
15 #include "source.h"
16 #include "flang/Common/idioms.h"
17 #include "flang/Common/interval.h"
18 #include "llvm/Support/raw_ostream.h"
19 #include <cstddef>
20 #include <map>
21 #include <memory>
22 #include <optional>
23 #include <string>
24 #include <utility>
25 #include <variant>
26 #include <vector>
27
28 namespace Fortran::parser {
29
30 // Each character in the contiguous source stream built by the
31 // prescanner corresponds to a particular character in a source file,
32 // include file, macro expansion, or compiler-inserted padding.
33 // The location of this original character to which a parsable character
34 // corresponds is its provenance.
35 //
36 // Provenances are offsets into an (unmaterialized) marshaling of the
37 // entire contents of all the original source files, include files, macro
38 // expansions, &c. for each visit to each source. These origins of the
39 // original source characters constitute a forest whose roots are
40 // the original source files named on the compiler's command line.
41 // Given a Provenance, we can find the tree node that contains it in time
42 // O(log(# of origins)), and describe the position precisely by walking
43 // up the tree. (It would be possible, via a time/space trade-off, to
44 // cap the time by the use of an intermediate table that would be indexed
45 // by the upper bits of an offset, but that does not appear to be
46 // necessary.)
47
48 class AllSources;
49
50 class Provenance {
51 public:
52 Provenance() {}
53 Provenance(std::size_t offset) : offset_{offset} { CHECK(offset > 0); }
54 Provenance(const Provenance &that) = default;
55 Provenance(Provenance &&that) = default;
56 Provenance &operator=(const Provenance &that) = default;
57 Provenance &operator=(Provenance &&that) = default;
58
59 std::size_t offset() const { return offset_; }
60
61 Provenance operator+(ptrdiff_t n) const {
62 CHECK(n > -static_cast<ptrdiff_t>(offset_));
63 return {offset_ + static_cast<std::size_t>(n)};
64 }
65 Provenance operator+(std::size_t n) const { return {offset_ + n}; }
66 std::size_t operator-(Provenance that) const {
67 CHECK(that <= *this);
68 return offset_ - that.offset_;
69 }
70 bool operator<(Provenance that) const { return offset_ < that.offset_; }
71 bool operator<=(Provenance that) const { return !(that < *this); }
72 bool operator==(Provenance that) const { return offset_ == that.offset_; }
73 bool operator!=(Provenance that) const { return !(*this == that); }
74
75 private:
76 std::size_t offset_{0};
77 };
78
79 using ProvenanceRange = common::Interval<Provenance>;
80
81 // Maps contiguous ranges of byte offsets in original source files to
82 // contiguous ranges in the cooked character stream; essentially a
83 // partial inversion of OffsetToProvenanceMappings (below).
84 // Used for implementing the first step of mapping an identifier
85 // selected in a code editor to one of its declarative statements.
86 class ProvenanceRangeToOffsetMappings {
87 public:
88 ProvenanceRangeToOffsetMappings();
89 ~ProvenanceRangeToOffsetMappings();
90 bool empty() const { return map_.empty(); }
91 void Put(ProvenanceRange, std::size_t offset);
92 std::optional<std::size_t> Map(ProvenanceRange) const;
93 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
94
95 private:
96 // A comparison function object for use in std::multimap<Compare=>.
97 // Intersecting intervals will effectively compare equal, not being
98 // either < nor >= each other.
99 struct WhollyPrecedes {
100 bool operator()(ProvenanceRange, ProvenanceRange) const;
101 };
102
103 std::multimap<ProvenanceRange, std::size_t, WhollyPrecedes> map_;
104 };
105
106 // Maps 0-based local offsets in some contiguous range (e.g., a token
107 // sequence) to their provenances. Lookup time is on the order of
108 // O(log(#of intervals with contiguous provenances)). As mentioned
109 // above, this time could be capped via a time/space trade-off.
110 class OffsetToProvenanceMappings {
111 public:
112 OffsetToProvenanceMappings() {}
113 void clear();
114 void swap(OffsetToProvenanceMappings &);
115 void shrink_to_fit();
116 std::size_t SizeInBytes() const;
117 void Put(ProvenanceRange);
118 void Put(const OffsetToProvenanceMappings &);
119 ProvenanceRange Map(std::size_t at) const;
120 void RemoveLastBytes(std::size_t);
121 ProvenanceRangeToOffsetMappings Invert(const AllSources &) const;
122 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
123
124 private:
125 struct ContiguousProvenanceMapping {
126 std::size_t start;
127 ProvenanceRange range;
128 };
129
130 // Elements appear in ascending order of distinct .start values;
131 // their .range values are disjoint and not necessarily adjacent.
132 std::vector<ContiguousProvenanceMapping> provenanceMap_;
133 };
134
135 // A singleton AllSources instance for the whole compilation
136 // is shared by reference.
137 class AllSources {
138 public:
139 AllSources();
140 ~AllSources();
141
142 std::size_t size() const { return range_.size(); }
143 const char &operator[](Provenance) const;
144 Encoding encoding() const { return encoding_; }
145 AllSources &set_encoding(Encoding e) {
146 encoding_ = e;
147 return *this;
148 }
149
150 void PushSearchPathDirectory(std::string);
151 std::string PopSearchPathDirectory();
152 const SourceFile *Open(std::string path, llvm::raw_ostream &error);
153 const SourceFile *ReadStandardInput(llvm::raw_ostream &error);
154
155 ProvenanceRange AddIncludedFile(
156 const SourceFile &, ProvenanceRange, bool isModule = false);
157 ProvenanceRange AddMacroCall(
158 ProvenanceRange def, ProvenanceRange use, const std::string &expansion);
159 ProvenanceRange AddCompilerInsertion(std::string);
160
161 bool IsValid(Provenance at) const { return range_.Contains(at); }
162 bool IsValid(ProvenanceRange range) const {
163 return range.size() > 0 && range_.Contains(range);
164 }
165 void EmitMessage(llvm::raw_ostream &, const std::optional<ProvenanceRange> &,
166 const std::string &message, bool echoSourceLine = false) const;
167 const SourceFile *GetSourceFile(
168 Provenance, std::size_t *offset = nullptr) const;
169 std::optional<SourcePosition> GetSourcePosition(Provenance) const;
170 std::optional<ProvenanceRange> GetFirstFileProvenance() const;
171 std::string GetPath(Provenance) const; // __FILE__
172 int GetLineNumber(Provenance) const; // __LINE__
173 Provenance CompilerInsertionProvenance(char ch);
174 Provenance CompilerInsertionProvenance(const char *, std::size_t);
175 ProvenanceRange IntersectionWithSourceFiles(ProvenanceRange) const;
176 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
177
178 private:
179 struct Inclusion {
180 const SourceFile &source;
181 bool isModule{false};
182 };
183 struct Macro {
184 ProvenanceRange definition;
185 std::string expansion;
186 };
187 struct CompilerInsertion {
188 std::string text;
189 };
190
191 struct Origin {
192 Origin(ProvenanceRange, const SourceFile &);
193 Origin(ProvenanceRange, const SourceFile &, ProvenanceRange,
194 bool isModule = false);
195 Origin(ProvenanceRange, ProvenanceRange def, ProvenanceRange use,
196 const std::string &expansion);
197 Origin(ProvenanceRange, const std::string &);
198
199 const char &operator[](std::size_t) const;
200
201 std::variant<Inclusion, Macro, CompilerInsertion> u;
202 ProvenanceRange covers, replaces;
203 };
204
205 const Origin &MapToOrigin(Provenance) const;
206
207 // Elements are in ascending & contiguous order of .covers.
208 std::vector<Origin> origin_;
209 ProvenanceRange range_;
210 std::map<char, Provenance> compilerInsertionProvenance_;
211 std::vector<std::unique_ptr<SourceFile>> ownedSourceFiles_;
212 std::vector<std::string> searchPath_;
213 Encoding encoding_{Encoding::UTF_8};
214 };
215
216 class CookedSource {
217 public:
218 explicit CookedSource(AllSources &);
219 ~CookedSource();
220
221 AllSources &allSources() { return allSources_; }
222 const AllSources &allSources() const { return allSources_; }
223 const std::string &data() const { return data_; }
224
225 bool IsValid(const char *p) const {
226 return p >= &data_.front() && p <= &data_.back() + 1;
227 }
228 bool IsValid(CharBlock range) const {
229 return !range.empty() && IsValid(range.begin()) && IsValid(range.end() - 1);
230 }
231 bool IsValid(ProvenanceRange r) const { return allSources_.IsValid(r); }
232
233 std::optional<ProvenanceRange> GetProvenanceRange(CharBlock) const;
234 std::optional<CharBlock> GetCharBlockFromLineAndColumns(
235 int line, int startColumn, int endColumn) const;
236 std::optional<std::pair<SourcePosition, SourcePosition>>
237 GetSourcePositionRange(CharBlock) const;
238 std::optional<CharBlock> GetCharBlock(ProvenanceRange) const;
239
240 // The result of a Put() is the offset that the new data
241 // will have in the eventually marshaled contiguous buffer.
242 std::size_t Put(const char *data, std::size_t bytes) {
243 return buffer_.Put(data, bytes);
244 }
245 std::size_t Put(const std::string &s) { return buffer_.Put(s); }
246 std::size_t Put(char ch) { return buffer_.Put(&ch, 1); }
247 std::size_t Put(char ch, Provenance p) {
248 provenanceMap_.Put(ProvenanceRange{p, 1});
249 return buffer_.Put(&ch, 1);
250 }
251
252 void PutProvenance(Provenance p) { provenanceMap_.Put(ProvenanceRange{p}); }
253 void PutProvenance(ProvenanceRange pr) { provenanceMap_.Put(pr); }
254 void PutProvenanceMappings(const OffsetToProvenanceMappings &pm) {
255 provenanceMap_.Put(pm);
256 }
257
258 std::size_t BufferedBytes() const;
259 void Marshal(); // marshals text into one contiguous block
260 void CompileProvenanceRangeToOffsetMappings();
261 std::string AcquireData() { return std::move(data_); }
262 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
263
264 private:
265 AllSources &allSources_;
266 CharBuffer buffer_; // before Marshal()
267 std::string data_; // all of it, prescanned and preprocessed
268 OffsetToProvenanceMappings provenanceMap_;
269 ProvenanceRangeToOffsetMappings invertedMap_;
270 };
271 } // namespace Fortran::parser
272 #endif // FORTRAN_PARSER_PROVENANCE_H_
Fortran::parser::SourceFile
Definition: source.h:41
Fortran::parser::AllSources
Definition: provenance.h:137
Fortran::parser::CookedSource
Definition: provenance.h:216
Fortran::parser::Provenance
Definition: provenance.h:50
Fortran::parser::OffsetToProvenanceMappings
Definition: provenance.h:110
Fortran::parser::CharBuffer
Definition: char-buffer.h:23
Fortran::parser::Map
Definition: parse-tree.h:3235
Fortran::parser::CharBlock
Definition: char-block.h:28
Fortran::common::Interval< Provenance >
Fortran::parser::ProvenanceRangeToOffsetMappings
Definition: provenance.h:86
Fortran::parser
Definition: check-expression.h:19