1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
11 #include "cmFortranParser.h"
12 #include "cmStringAlgorithms.h"
13 #include "cmSystemTools.h"
15 bool cmFortranParser_s::FindIncludeFile(const char* dir,
16 const char* includeName,
17 std::string& fileName)
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);
24 // Check for the file in the directory containing the including
26 std::string fullName = cmStrCat(dir, '/', includeName);
27 if (cmSystemTools::FileExists(fullName, true)) {
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)) {
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))
52 this->InInterface = false;
53 this->InPPFalseBranch = 0;
55 // Initialize the lexical scanner.
56 cmFortran_yylex_init(&this->Scanner);
57 cmFortran_yyset_extra(this, this->Scanner);
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);
66 cmFortranParser_s::~cmFortranParser_s()
68 cmFortran_yylex_destroy(this->Scanner);
71 std::string cmFortranParser_s::ModName(std::string const& mod_name) const
73 return mod_name + ".mod";
76 std::string cmFortranParser_s::SModName(std::string const& mod_name,
77 std::string const& sub_name) const
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;
85 return mod_name + this->Compiler.SModSep + sub_name + SModExt;
88 bool cmFortranParser_FilePush(cmFortranParser* parser, const char* fname)
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);
105 bool cmFortranParser_FilePop(cmFortranParser* parser)
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()) {
112 cmFortranFile f = parser->FileStack.top();
113 parser->FileStack.pop();
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);
121 int cmFortranParser_Input(cmFortranParser* parser, char* buffer,
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);
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.
137 ff.LastCharWasNewline = true;
139 return static_cast<int>(n);
144 void cmFortranParser_StringStart(cmFortranParser* parser)
146 parser->TokenString.clear();
149 const char* cmFortranParser_StringEnd(cmFortranParser* parser)
151 return parser->TokenString.c_str();
154 void cmFortranParser_StringAppend(cmFortranParser* parser, char c)
156 parser->TokenString += c;
159 void cmFortranParser_SetInInterface(cmFortranParser* parser, bool in)
161 if (parser->InPPFalseBranch) {
165 parser->InInterface = in;
168 bool cmFortranParser_GetInInterface(cmFortranParser* parser)
170 return parser->InInterface;
173 void cmFortranParser_SetOldStartcond(cmFortranParser* parser, int arg)
175 parser->OldStartcond = arg;
178 int cmFortranParser_GetOldStartcond(cmFortranParser* parser)
180 return parser->OldStartcond;
183 void cmFortranParser_Error(cmFortranParser* parser, const char* msg)
185 parser->Error = msg ? msg : "unknown error";
188 void cmFortranParser_RuleUse(cmFortranParser* parser, const char* module_name)
190 if (parser->InPPFalseBranch) {
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));
200 void cmFortranParser_RuleUseIntrinsic(cmFortranParser* parser,
201 const char* module_name)
203 if (parser->InPPFalseBranch) {
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));
213 void cmFortranParser_RuleLineDirective(cmFortranParser* parser,
214 const char* filename)
216 // This is a #line directive naming a file encountered during preprocessing.
217 std::string included = filename;
219 // Skip #line directives referencing non-files like
220 // "<built-in>" or "<command-line>".
221 if (included.empty() || included[0] == '<') {
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);
230 // Save the named file as included in the source.
231 if (cmSystemTools::FileExists(included, true)) {
232 parser->Info.Includes.insert(included);
236 void cmFortranParser_RuleInclude(cmFortranParser* parser, const char* name)
238 if (parser->InPPFalseBranch) {
242 // If processing an include statement there must be an open file.
243 assert(!parser->FileStack.empty());
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;
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);
258 // Parse it immediately to translate the source inline.
259 cmFortranParser_FilePush(parser, fullName.c_str());
263 void cmFortranParser_RuleModule(cmFortranParser* parser,
264 const char* module_name)
266 if (parser->InPPFalseBranch) {
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));
278 void cmFortranParser_RuleSubmodule(cmFortranParser* parser,
279 const char* module_name,
280 const char* submodule_name)
282 if (parser->InPPFalseBranch) {
286 // syntax: "submodule (module_name) submodule_name"
287 // requires: "module_name.mod"
288 // provides: "module_name@submodule_name.smod"
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".
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));
301 void cmFortranParser_RuleSubmoduleNested(cmFortranParser* parser,
302 const char* module_name,
303 const char* submodule_name,
304 const char* nested_submodule_name)
306 if (parser->InPPFalseBranch) {
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"
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));
322 void cmFortranParser_RuleDefine(cmFortranParser* parser, const char* macro)
324 if (!parser->InPPFalseBranch) {
325 parser->PPDefinitions.insert(macro);
329 void cmFortranParser_RuleUndef(cmFortranParser* parser, const char* macro)
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);
340 void cmFortranParser_RuleIfdef(cmFortranParser* parser, const char* macro)
342 // A new PP branch has been opened
343 parser->SkipToEnd.push(false);
345 if (parser->InPPFalseBranch) {
346 parser->InPPFalseBranch++;
347 } else if (parser->PPDefinitions.find(macro) ==
348 parser->PPDefinitions.end()) {
349 parser->InPPFalseBranch = 1;
351 parser->SkipToEnd.top() = true;
355 void cmFortranParser_RuleIfndef(cmFortranParser* parser, const char* macro)
357 // A new PP branch has been opened
358 parser->SkipToEnd.push(false);
360 if (parser->InPPFalseBranch) {
361 parser->InPPFalseBranch++;
362 } else if (parser->PPDefinitions.find(macro) !=
363 parser->PPDefinitions.end()) {
364 parser->InPPFalseBranch = 1;
366 // ignore other branches
367 parser->SkipToEnd.top() = true;
371 void cmFortranParser_RuleIf(cmFortranParser* parser)
373 /* Note: The current parser is _not_ able to get statements like
377 * #if defined(MYSYMBOL)
378 * #if defined(MYSYMBOL) && ...
379 * right. The same for #elif. Thus in
390 * _all_ N+1 branches are considered. If you got something like this
391 * #if defined(MYSYMBOL)
392 * #if !defined(MYSYMBOL)
399 // A new PP branch has been opened
400 // Never skip! See note above.
401 parser->SkipToEnd.push(false);
404 void cmFortranParser_RuleElif(cmFortranParser* parser)
406 /* Note: There are parser limitations. See the note at
407 * cmFortranParser_RuleIf(..)
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;
419 void cmFortranParser_RuleElse(cmFortranParser* parser)
421 // if the parent branch is false do nothing!
422 if (parser->InPPFalseBranch > 1) {
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;
431 parser->InPPFalseBranch = 0;
435 void cmFortranParser_RuleEndif(cmFortranParser* parser)
437 if (!parser->SkipToEnd.empty()) {
438 parser->SkipToEnd.pop();
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--;