/src/CMake/Source/cmFortranParserImpl.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include <cassert> |
4 | | #include <cstdio> |
5 | | #include <set> |
6 | | #include <stack> |
7 | | #include <string> |
8 | | #include <utility> |
9 | | #include <vector> |
10 | | |
11 | | #include "cmFortranParser.h" |
12 | | #include "cmStringAlgorithms.h" |
13 | | #include "cmSystemTools.h" |
14 | | |
15 | | bool cmFortranParser_s::FindIncludeFile(char const* dir, |
16 | | char const* includeName, |
17 | | std::string& fileName) |
18 | 0 | { |
19 | | // If the file is a full path, include it directly. |
20 | 0 | if (cmSystemTools::FileIsFullPath(includeName)) { |
21 | 0 | fileName = includeName; |
22 | 0 | return cmSystemTools::FileExists(fileName, true); |
23 | 0 | } |
24 | | // Check for the file in the directory containing the including |
25 | | // file. |
26 | 0 | std::string fullName = cmStrCat(dir, '/', includeName); |
27 | 0 | if (cmSystemTools::FileExists(fullName, true)) { |
28 | 0 | fileName = fullName; |
29 | 0 | return true; |
30 | 0 | } |
31 | | |
32 | | // Search the include path for the file. |
33 | 0 | for (std::string const& i : this->IncludePath) { |
34 | 0 | fullName = cmStrCat(i, '/', includeName); |
35 | 0 | if (cmSystemTools::FileExists(fullName, true)) { |
36 | 0 | fileName = fullName; |
37 | 0 | return true; |
38 | 0 | } |
39 | 0 | } |
40 | 0 | return false; |
41 | 0 | } |
42 | | |
43 | | cmFortranParser_s::cmFortranParser_s(cmFortranCompiler fc, |
44 | | std::vector<std::string> includes, |
45 | | std::set<std::string> defines, |
46 | | cmFortranSourceInfo& info) |
47 | 0 | : Compiler(std::move(fc)) |
48 | 0 | , IncludePath(std::move(includes)) |
49 | 0 | , PPDefinitions(std::move(defines)) |
50 | 0 | , Info(info) |
51 | 0 | { |
52 | 0 | this->InInterface = false; |
53 | 0 | this->InPPFalseBranch = 0; |
54 | | |
55 | | // Initialize the lexical scanner. |
56 | 0 | cmFortran_yylex_init(&this->Scanner); |
57 | 0 | cmFortran_yyset_extra(this, this->Scanner); |
58 | | |
59 | | // Create a dummy buffer that is never read but is the fallback |
60 | | // buffer when the last file is popped off the stack. |
61 | 0 | YY_BUFFER_STATE buffer = |
62 | 0 | cmFortran_yy_create_buffer(nullptr, 4, this->Scanner); |
63 | 0 | cmFortran_yy_switch_to_buffer(buffer, this->Scanner); |
64 | 0 | } |
65 | | |
66 | | cmFortranParser_s::~cmFortranParser_s() |
67 | 0 | { |
68 | 0 | cmFortran_yylex_destroy(this->Scanner); |
69 | 0 | } |
70 | | |
71 | | std::string cmFortranParser_s::ModName(std::string const& mod_name) const |
72 | 0 | { |
73 | 0 | return mod_name + ".mod"; |
74 | 0 | } |
75 | | |
76 | | std::string cmFortranParser_s::SModName(std::string const& mod_name, |
77 | | std::string const& sub_name) const |
78 | 0 | { |
79 | 0 | std::string const& SModExt = |
80 | 0 | this->Compiler.SModExt.empty() ? ".mod" : this->Compiler.SModExt; |
81 | | // An empty separator means that the compiler does not use a prefix. |
82 | 0 | if (this->Compiler.SModSep.empty()) { |
83 | 0 | return sub_name + SModExt; |
84 | 0 | } |
85 | 0 | return mod_name + this->Compiler.SModSep + sub_name + SModExt; |
86 | 0 | } |
87 | | |
88 | | bool cmFortranParser_FilePush(cmFortranParser* parser, char const* fname) |
89 | 0 | { |
90 | | // Do not revisit already processed files; treat them as non-existing. |
91 | | // Avoids infinite recursion caused by misinterpreted include guards. |
92 | 0 | if (parser->VisitedFilePaths.insert(fname).second) { |
93 | | // Open the new file and push it onto the stack. Save the old |
94 | | // buffer with it on the stack. |
95 | 0 | if (FILE* file = cmsys::SystemTools::Fopen(fname, "rb")) { |
96 | 0 | YY_BUFFER_STATE current = |
97 | 0 | cmFortranLexer_GetCurrentBuffer(parser->Scanner); |
98 | 0 | std::string dir = cmSystemTools::GetParentDirectory(fname); |
99 | 0 | cmFortranFile f(file, current, dir); |
100 | 0 | YY_BUFFER_STATE buffer = |
101 | 0 | cmFortran_yy_create_buffer(nullptr, 16384, parser->Scanner); |
102 | 0 | cmFortran_yy_switch_to_buffer(buffer, parser->Scanner); |
103 | 0 | parser->FileStack.push(f); |
104 | 0 | return true; |
105 | 0 | } |
106 | 0 | } |
107 | 0 | return false; |
108 | 0 | } |
109 | | |
110 | | bool cmFortranParser_FilePop(cmFortranParser* parser) |
111 | 0 | { |
112 | | // Pop one file off the stack and close it. Switch the lexer back |
113 | | // to the next one on the stack. |
114 | 0 | if (parser->FileStack.empty()) { |
115 | 0 | return false; |
116 | 0 | } |
117 | 0 | cmFortranFile f = parser->FileStack.top(); |
118 | 0 | parser->FileStack.pop(); |
119 | 0 | fclose(f.File); |
120 | 0 | YY_BUFFER_STATE current = cmFortranLexer_GetCurrentBuffer(parser->Scanner); |
121 | 0 | cmFortran_yy_delete_buffer(current, parser->Scanner); |
122 | 0 | cmFortran_yy_switch_to_buffer(f.Buffer, parser->Scanner); |
123 | 0 | return true; |
124 | 0 | } |
125 | | |
126 | | int cmFortranParser_Input(cmFortranParser* parser, char* buffer, |
127 | | size_t bufferSize) |
128 | 0 | { |
129 | | // Read from the file on top of the stack. If the stack is empty, |
130 | | // the end of the translation unit has been reached. |
131 | 0 | if (!parser->FileStack.empty()) { |
132 | 0 | cmFortranFile& ff = parser->FileStack.top(); |
133 | 0 | FILE* file = ff.File; |
134 | 0 | size_t n = fread(buffer, 1, bufferSize, file); |
135 | 0 | if (n > 0) { |
136 | 0 | ff.LastCharWasNewline = buffer[n - 1] == '\n'; |
137 | 0 | } else if (!ff.LastCharWasNewline) { |
138 | | // The file ended without a newline. Inject one so |
139 | | // that the file always ends in an end-of-statement. |
140 | 0 | buffer[0] = '\n'; |
141 | 0 | n = 1; |
142 | 0 | ff.LastCharWasNewline = true; |
143 | 0 | } |
144 | 0 | return static_cast<int>(n); |
145 | 0 | } |
146 | 0 | return 0; |
147 | 0 | } |
148 | | |
149 | | void cmFortranParser_StringStart(cmFortranParser* parser) |
150 | 0 | { |
151 | 0 | parser->TokenString.clear(); |
152 | 0 | } |
153 | | |
154 | | char const* cmFortranParser_StringEnd(cmFortranParser* parser) |
155 | 0 | { |
156 | 0 | return parser->TokenString.c_str(); |
157 | 0 | } |
158 | | |
159 | | void cmFortranParser_StringAppend(cmFortranParser* parser, char c) |
160 | 0 | { |
161 | 0 | parser->TokenString += c; |
162 | 0 | } |
163 | | |
164 | | void cmFortranParser_SetInInterface(cmFortranParser* parser, bool in) |
165 | 0 | { |
166 | 0 | if (parser->InPPFalseBranch) { |
167 | 0 | return; |
168 | 0 | } |
169 | | |
170 | 0 | parser->InInterface = in; |
171 | 0 | } |
172 | | |
173 | | bool cmFortranParser_GetInInterface(cmFortranParser* parser) |
174 | 0 | { |
175 | 0 | return parser->InInterface; |
176 | 0 | } |
177 | | |
178 | | void cmFortranParser_SetOldStartcond(cmFortranParser* parser, int arg) |
179 | 0 | { |
180 | 0 | parser->OldStartcond = arg; |
181 | 0 | } |
182 | | |
183 | | int cmFortranParser_GetOldStartcond(cmFortranParser* parser) |
184 | 0 | { |
185 | 0 | return parser->OldStartcond; |
186 | 0 | } |
187 | | |
188 | | void cmFortranParser_Error(cmFortranParser* parser, char const* msg) |
189 | 0 | { |
190 | 0 | parser->Error = msg ? msg : "unknown error"; |
191 | 0 | } |
192 | | |
193 | | void cmFortranParser_RuleUse(cmFortranParser* parser, char const* module_name) |
194 | 0 | { |
195 | 0 | if (parser->InPPFalseBranch) { |
196 | 0 | return; |
197 | 0 | } |
198 | | |
199 | | // syntax: "use module_name" |
200 | | // requires: "module_name.mod" |
201 | 0 | std::string const& mod_name = cmSystemTools::LowerCase(module_name); |
202 | 0 | parser->Info.Requires.insert(parser->ModName(mod_name)); |
203 | 0 | } |
204 | | |
205 | | void cmFortranParser_RuleUseIntrinsic(cmFortranParser* parser, |
206 | | char const* module_name) |
207 | 0 | { |
208 | 0 | if (parser->InPPFalseBranch) { |
209 | 0 | return; |
210 | 0 | } |
211 | | |
212 | | // syntax: "use, intrinsic:: module_name" |
213 | | // requires: "module_name.mod" |
214 | 0 | std::string const& mod_name = cmSystemTools::LowerCase(module_name); |
215 | 0 | parser->Info.Intrinsics.insert(parser->ModName(mod_name)); |
216 | 0 | } |
217 | | |
218 | | void cmFortranParser_RuleLineDirective(cmFortranParser* parser, |
219 | | char const* filename) |
220 | 0 | { |
221 | | // This is a #line directive naming a file encountered during preprocessing. |
222 | 0 | std::string included = filename; |
223 | | |
224 | | // Skip #line directives referencing non-files like |
225 | | // "<built-in>" or "<command-line>". |
226 | 0 | if (included.empty() || included[0] == '<') { |
227 | 0 | return; |
228 | 0 | } |
229 | | |
230 | | // Fix windows file path separators since our lexer does not |
231 | | // process escape sequences in string literals. |
232 | 0 | cmSystemTools::ReplaceString(included, "\\\\", "\\"); |
233 | 0 | cmSystemTools::ConvertToUnixSlashes(included); |
234 | | |
235 | | // Save the named file as included in the source. |
236 | 0 | if (cmSystemTools::FileExists(included, true)) { |
237 | 0 | parser->Info.Includes.insert(included); |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | | void cmFortranParser_RuleInclude(cmFortranParser* parser, char const* name) |
242 | 0 | { |
243 | 0 | if (parser->InPPFalseBranch) { |
244 | 0 | return; |
245 | 0 | } |
246 | | |
247 | | // If processing an include statement there must be an open file. |
248 | 0 | assert(!parser->FileStack.empty()); |
249 | | |
250 | | // Get the directory containing the source in which the include |
251 | | // statement appears. This is always the first search location for |
252 | | // Fortran include files. |
253 | 0 | std::string dir = parser->FileStack.top().Directory; |
254 | | |
255 | | // Find the included file. If it cannot be found just ignore the |
256 | | // problem because either the source will not compile or the user |
257 | | // does not care about depending on this included source. |
258 | 0 | std::string fullName; |
259 | 0 | if (parser->FindIncludeFile(dir.c_str(), name, fullName)) { |
260 | | // Found the included file. Save it in the set of included files. |
261 | 0 | parser->Info.Includes.insert(fullName); |
262 | | |
263 | | // Parse it immediately to translate the source inline. |
264 | 0 | cmFortranParser_FilePush(parser, fullName.c_str()); |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | | void cmFortranParser_RuleModule(cmFortranParser* parser, |
269 | | char const* module_name) |
270 | 0 | { |
271 | 0 | if (parser->InPPFalseBranch) { |
272 | 0 | return; |
273 | 0 | } |
274 | | |
275 | 0 | if (!parser->InInterface) { |
276 | | // syntax: "module module_name" |
277 | | // provides: "module_name.mod" |
278 | 0 | std::string const& mod_name = cmSystemTools::LowerCase(module_name); |
279 | 0 | parser->Info.Provides.insert(parser->ModName(mod_name)); |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | | void cmFortranParser_RuleSubmodule(cmFortranParser* parser, |
284 | | char const* module_name, |
285 | | char const* submodule_name) |
286 | 0 | { |
287 | 0 | if (parser->InPPFalseBranch) { |
288 | 0 | return; |
289 | 0 | } |
290 | | |
291 | | // syntax: "submodule (module_name) submodule_name" |
292 | | // requires: "module_name.mod" |
293 | | // provides: "module_name@submodule_name.smod" |
294 | | // |
295 | | // FIXME: Some compilers split the submodule part of a module into a |
296 | | // separate "module_name.smod" file. Whether it is generated or |
297 | | // not depends on conditions more subtle than we currently detect. |
298 | | // For now we depend directly on "module_name.mod". |
299 | | |
300 | 0 | std::string const& mod_name = cmSystemTools::LowerCase(module_name); |
301 | 0 | std::string const& sub_name = cmSystemTools::LowerCase(submodule_name); |
302 | 0 | parser->Info.Requires.insert(parser->ModName(mod_name)); |
303 | 0 | parser->Info.Provides.insert(parser->SModName(mod_name, sub_name)); |
304 | 0 | } |
305 | | |
306 | | void cmFortranParser_RuleSubmoduleNested(cmFortranParser* parser, |
307 | | char const* module_name, |
308 | | char const* submodule_name, |
309 | | char const* nested_submodule_name) |
310 | 0 | { |
311 | 0 | if (parser->InPPFalseBranch) { |
312 | 0 | return; |
313 | 0 | } |
314 | | |
315 | | // syntax: "submodule (module_name:submodule_name) nested_submodule_name" |
316 | | // requires: "module_name@submodule_name.smod" |
317 | | // provides: "module_name@nested_submodule_name.smod" |
318 | | |
319 | 0 | std::string const& mod_name = cmSystemTools::LowerCase(module_name); |
320 | 0 | std::string const& sub_name = cmSystemTools::LowerCase(submodule_name); |
321 | 0 | std::string const& nest_name = |
322 | 0 | cmSystemTools::LowerCase(nested_submodule_name); |
323 | 0 | parser->Info.Requires.insert(parser->SModName(mod_name, sub_name)); |
324 | 0 | parser->Info.Provides.insert(parser->SModName(mod_name, nest_name)); |
325 | 0 | } |
326 | | |
327 | | void cmFortranParser_RuleDefine(cmFortranParser* parser, char const* macro) |
328 | 0 | { |
329 | 0 | if (!parser->InPPFalseBranch) { |
330 | 0 | parser->PPDefinitions.insert(macro); |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | void cmFortranParser_RuleUndef(cmFortranParser* parser, char const* macro) |
335 | 0 | { |
336 | 0 | if (!parser->InPPFalseBranch) { |
337 | 0 | std::set<std::string>::iterator match; |
338 | 0 | match = parser->PPDefinitions.find(macro); |
339 | 0 | if (match != parser->PPDefinitions.end()) { |
340 | 0 | parser->PPDefinitions.erase(match); |
341 | 0 | } |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | void cmFortranParser_RuleIfdef(cmFortranParser* parser, char const* macro) |
346 | 0 | { |
347 | | // A new PP branch has been opened |
348 | 0 | parser->SkipToEnd.push(false); |
349 | |
|
350 | 0 | if (parser->InPPFalseBranch) { |
351 | 0 | parser->InPPFalseBranch++; |
352 | 0 | } else if (parser->PPDefinitions.find(macro) == |
353 | 0 | parser->PPDefinitions.end()) { |
354 | 0 | parser->InPPFalseBranch = 1; |
355 | 0 | } else { |
356 | 0 | parser->SkipToEnd.top() = true; |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | | void cmFortranParser_RuleIfndef(cmFortranParser* parser, char const* macro) |
361 | 0 | { |
362 | | // A new PP branch has been opened |
363 | 0 | parser->SkipToEnd.push(false); |
364 | |
|
365 | 0 | if (parser->InPPFalseBranch) { |
366 | 0 | parser->InPPFalseBranch++; |
367 | 0 | } else if (parser->PPDefinitions.find(macro) != |
368 | 0 | parser->PPDefinitions.end()) { |
369 | 0 | parser->InPPFalseBranch = 1; |
370 | 0 | } else { |
371 | | // ignore other branches |
372 | 0 | parser->SkipToEnd.top() = true; |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | | void cmFortranParser_RuleIf(cmFortranParser* parser) |
377 | 0 | { |
378 | | /* Note: The current parser is _not_ able to get statements like |
379 | | * #if 0 |
380 | | * #if 1 |
381 | | * #if MYSMBOL |
382 | | * #if defined(MYSYMBOL) |
383 | | * #if defined(MYSYMBOL) && ... |
384 | | * right. The same for #elif. Thus in |
385 | | * #if SYMBOL_1 |
386 | | * .. |
387 | | * #elif SYMBOL_2 |
388 | | * ... |
389 | | * ... |
390 | | * #elif SYMBOL_N |
391 | | * .. |
392 | | * #else |
393 | | * .. |
394 | | * #endif |
395 | | * _all_ N+1 branches are considered. If you got something like this |
396 | | * #if defined(MYSYMBOL) |
397 | | * #if !defined(MYSYMBOL) |
398 | | * use |
399 | | * #ifdef MYSYMBOL |
400 | | * #ifndef MYSYMBOL |
401 | | * instead. |
402 | | */ |
403 | | |
404 | | // A new PP branch has been opened |
405 | | // Never skip! See note above. |
406 | 0 | parser->SkipToEnd.push(false); |
407 | 0 | } |
408 | | |
409 | | void cmFortranParser_RuleElif(cmFortranParser* parser) |
410 | 0 | { |
411 | | /* Note: There are parser limitations. See the note at |
412 | | * cmFortranParser_RuleIf(..) |
413 | | */ |
414 | | |
415 | | // Always taken unless an #ifdef or #ifndef-branch has been taken |
416 | | // already. If the second condition isn't meet already |
417 | | // (parser->InPPFalseBranch == 0) correct it. |
418 | 0 | if (!parser->SkipToEnd.empty() && parser->SkipToEnd.top() && |
419 | 0 | !parser->InPPFalseBranch) { |
420 | 0 | parser->InPPFalseBranch = 1; |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | | void cmFortranParser_RuleElse(cmFortranParser* parser) |
425 | 0 | { |
426 | | // if the parent branch is false do nothing! |
427 | 0 | if (parser->InPPFalseBranch > 1) { |
428 | 0 | return; |
429 | 0 | } |
430 | | |
431 | | // parser->InPPFalseBranch is either 0 or 1. We change it depending on |
432 | | // parser->SkipToEnd.top() |
433 | 0 | if (!parser->SkipToEnd.empty() && parser->SkipToEnd.top()) { |
434 | 0 | parser->InPPFalseBranch = 1; |
435 | 0 | } else { |
436 | 0 | parser->InPPFalseBranch = 0; |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | | void cmFortranParser_RuleEndif(cmFortranParser* parser) |
441 | 0 | { |
442 | 0 | if (!parser->SkipToEnd.empty()) { |
443 | 0 | parser->SkipToEnd.pop(); |
444 | 0 | } |
445 | | |
446 | | // #endif doesn't know if there was a "#else" in before, so it |
447 | | // always decreases InPPFalseBranch |
448 | 0 | if (parser->InPPFalseBranch) { |
449 | 0 | parser->InPPFalseBranch--; |
450 | 0 | } |
451 | 0 | } |