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