cmFortranParserImpl.cxx 12.7 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3 4
#include "cmFortranParser.h"
#include "cmSystemTools.h"
5

6
#include <assert.h>
7 8 9 10
#include <set>
#include <stack>
#include <stdio.h>
#include <string>
wahikihiki's avatar
wahikihiki committed
11
#include <utility>
12
#include <vector>
13 14 15 16 17 18

bool cmFortranParser_s::FindIncludeFile(const char* dir,
                                        const char* includeName,
                                        std::string& fileName)
{
  // If the file is a full path, include it directly.
19
  if (cmSystemTools::FileIsFullPath(includeName)) {
20
    fileName = includeName;
21
    return cmSystemTools::FileExists(fileName, true);
22 23 24 25 26 27
  }
  // Check for the file in the directory containing the including
  // file.
  std::string fullName = dir;
  fullName += "/";
  fullName += includeName;
28
  if (cmSystemTools::FileExists(fullName, true)) {
29 30 31 32 33
    fileName = fullName;
    return true;
  }

  // Search the include path for the file.
34 35
  for (std::string const& i : this->IncludePath) {
    fullName = i;
36 37
    fullName += "/";
    fullName += includeName;
38
    if (cmSystemTools::FileExists(fullName, true)) {
39 40
      fileName = fullName;
      return true;
41 42
    }
  }
43 44 45
  return false;
}

46 47
cmFortranParser_s::cmFortranParser_s(cmFortranCompiler fc,
                                     std::vector<std::string> includes,
wahikihiki's avatar
wahikihiki committed
48
                                     std::set<std::string> defines,
49
                                     cmFortranSourceInfo& info)
50 51
  : Compiler(std::move(fc))
  , IncludePath(std::move(includes))
wahikihiki's avatar
wahikihiki committed
52
  , PPDefinitions(std::move(defines))
53
  , Info(info)
54
{
55
  this->InInterface = false;
56 57 58 59 60 61 62 63
  this->InPPFalseBranch = 0;

  // Initialize the lexical scanner.
  cmFortran_yylex_init(&this->Scanner);
  cmFortran_yyset_extra(this, this->Scanner);

  // Create a dummy buffer that is never read but is the fallback
  // buffer when the last file is popped off the stack.
Daniel Pfeifer's avatar
Daniel Pfeifer committed
64
  YY_BUFFER_STATE buffer =
Daniel Pfeifer's avatar
Daniel Pfeifer committed
65
    cmFortran_yy_create_buffer(nullptr, 4, this->Scanner);
66 67 68 69 70 71 72 73
  cmFortran_yy_switch_to_buffer(buffer, this->Scanner);
}

cmFortranParser_s::~cmFortranParser_s()
{
  cmFortran_yylex_destroy(this->Scanner);
}

74 75 76 77 78 79 80 81
std::string cmFortranParser_s::ModName(std::string const& mod_name) const
{
  return mod_name + ".mod";
}

std::string cmFortranParser_s::SModName(std::string const& mod_name,
                                        std::string const& sub_name) const
{
82 83 84 85 86 87 88
  std::string const& SModExt =
    this->Compiler.SModExt.empty() ? ".mod" : this->Compiler.SModExt;
  // An empty separator means that the compiler does not use a prefix.
  if (this->Compiler.SModSep.empty()) {
    return sub_name + SModExt;
  }
  return mod_name + this->Compiler.SModSep + sub_name + SModExt;
89 90
}

91
bool cmFortranParser_FilePush(cmFortranParser* parser, const char* fname)
92 93 94
{
  // Open the new file and push it onto the stack.  Save the old
  // buffer with it on the stack.
95 96
  if (FILE* file = cmsys::SystemTools::Fopen(fname, "rb")) {
    YY_BUFFER_STATE current = cmFortranLexer_GetCurrentBuffer(parser->Scanner);
97 98 99
    std::string dir = cmSystemTools::GetParentDirectory(fname);
    cmFortranFile f(file, current, dir);
    YY_BUFFER_STATE buffer =
Daniel Pfeifer's avatar
Daniel Pfeifer committed
100
      cmFortran_yy_create_buffer(nullptr, 16384, parser->Scanner);
101 102
    cmFortran_yy_switch_to_buffer(buffer, parser->Scanner);
    parser->FileStack.push(f);
103
    return true;
104
  }
105
  return false;
106 107 108 109 110 111
}

bool cmFortranParser_FilePop(cmFortranParser* parser)
{
  // Pop one file off the stack and close it.  Switch the lexer back
  // to the next one on the stack.
112
  if (parser->FileStack.empty()) {
113
    return false;
114
  }
115 116 117 118 119 120
  cmFortranFile f = parser->FileStack.top();
  parser->FileStack.pop();
  fclose(f.File);
  YY_BUFFER_STATE current = cmFortranLexer_GetCurrentBuffer(parser->Scanner);
  cmFortran_yy_delete_buffer(current, parser->Scanner);
  cmFortran_yy_switch_to_buffer(f.Buffer, parser->Scanner);
121
  return true;
122 123
}

124 125
int cmFortranParser_Input(cmFortranParser* parser, char* buffer,
                          size_t bufferSize)
126 127 128
{
  // Read from the file on top of the stack.  If the stack is empty,
  // the end of the translation unit has been reached.
129
  if (!parser->FileStack.empty()) {
130 131 132 133 134 135 136 137 138 139 140 141
    cmFortranFile& ff = parser->FileStack.top();
    FILE* file = ff.File;
    size_t n = fread(buffer, 1, bufferSize, file);
    if (n > 0) {
      ff.LastCharWasNewline = buffer[n - 1] == '\n';
    } else if (!ff.LastCharWasNewline) {
      // The file ended without a newline.  Inject one so
      // that the file always ends in an end-of-statement.
      buffer[0] = '\n';
      n = 1;
      ff.LastCharWasNewline = true;
    }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
142
    return static_cast<int>(n);
143
  }
144 145 146 147 148
  return 0;
}

void cmFortranParser_StringStart(cmFortranParser* parser)
{
149
  parser->TokenString.clear();
150 151 152 153 154 155 156
}

const char* cmFortranParser_StringEnd(cmFortranParser* parser)
{
  return parser->TokenString.c_str();
}

157
void cmFortranParser_StringAppend(cmFortranParser* parser, char c)
158 159 160 161
{
  parser->TokenString += c;
}

162
void cmFortranParser_SetInInterface(cmFortranParser* parser, bool in)
163
{
164
  if (parser->InPPFalseBranch) {
165
    return;
166
  }
167 168 169 170 171 172 173 174 175

  parser->InInterface = in;
}

bool cmFortranParser_GetInInterface(cmFortranParser* parser)
{
  return parser->InInterface;
}

176
void cmFortranParser_SetOldStartcond(cmFortranParser* parser, int arg)
177 178 179 180 181 182 183 184 185
{
  parser->OldStartcond = arg;
}

int cmFortranParser_GetOldStartcond(cmFortranParser* parser)
{
  return parser->OldStartcond;
}

186
void cmFortranParser_Error(cmFortranParser* parser, const char* msg)
187
{
188
  parser->Error = msg ? msg : "unknown error";
189 190
}

191
void cmFortranParser_RuleUse(cmFortranParser* parser, const char* module_name)
192
{
193 194
  if (parser->InPPFalseBranch) {
    return;
195
  }
196 197 198 199

  // syntax:   "use module_name"
  // requires: "module_name.mod"
  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
200
  parser->Info.Requires.insert(parser->ModName(mod_name));
201 202
}

203 204 205 206 207 208 209 210
void cmFortranParser_RuleLineDirective(cmFortranParser* parser,
                                       const char* filename)
{
  // This is a #line directive naming a file encountered during preprocessing.
  std::string included = filename;

  // Skip #line directives referencing non-files like
  // "<built-in>" or "<command-line>".
211
  if (included.empty() || included[0] == '<') {
212
    return;
213
  }
214 215 216 217 218 219 220

  // Fix windows file path separators since our lexer does not
  // process escape sequences in string literals.
  cmSystemTools::ReplaceString(included, "\\\\", "\\");
  cmSystemTools::ConvertToUnixSlashes(included);

  // Save the named file as included in the source.
221
  if (cmSystemTools::FileExists(included, true)) {
222
    parser->Info.Includes.insert(included);
223
  }
224 225
}

226
void cmFortranParser_RuleInclude(cmFortranParser* parser, const char* name)
227
{
228
  if (parser->InPPFalseBranch) {
229
    return;
230
  }
231 232 233 234 235 236 237 238 239 240 241 242 243

  // If processing an include statement there must be an open file.
  assert(!parser->FileStack.empty());

  // Get the directory containing the source in which the include
  // statement appears.  This is always the first search location for
  // Fortran include files.
  std::string dir = parser->FileStack.top().Directory;

  // Find the included file.  If it cannot be found just ignore the
  // problem because either the source will not compile or the user
  // does not care about depending on this included source.
  std::string fullName;
244
  if (parser->FindIncludeFile(dir.c_str(), name, fullName)) {
245 246 247 248 249
    // Found the included file.  Save it in the set of included files.
    parser->Info.Includes.insert(fullName);

    // Parse it immediately to translate the source inline.
    cmFortranParser_FilePush(parser, fullName.c_str());
250
  }
251 252
}

253 254
void cmFortranParser_RuleModule(cmFortranParser* parser,
                                const char* module_name)
255
{
256 257 258 259 260 261 262 263
  if (parser->InPPFalseBranch) {
    return;
  }

  if (!parser->InInterface) {
    // syntax:   "module module_name"
    // provides: "module_name.mod"
    std::string const& mod_name = cmSystemTools::LowerCase(module_name);
264
    parser->Info.Provides.insert(parser->ModName(mod_name));
265
  }
266 267
}

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
void cmFortranParser_RuleSubmodule(cmFortranParser* parser,
                                   const char* module_name,
                                   const char* submodule_name)
{
  if (parser->InPPFalseBranch) {
    return;
  }

  // syntax:   "submodule (module_name) submodule_name"
  // requires: "module_name.mod"
  // provides: "module_name@submodule_name.smod"
  //
  // FIXME: Some compilers split the submodule part of a module into a
  // separate "module_name.smod" file.  Whether it is generated or
  // not depends on conditions more subtle than we currently detect.
  // For now we depend directly on "module_name.mod".

  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
  std::string const& sub_name = cmSystemTools::LowerCase(submodule_name);
287 288
  parser->Info.Requires.insert(parser->ModName(mod_name));
  parser->Info.Provides.insert(parser->SModName(mod_name, sub_name));
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
}

void cmFortranParser_RuleSubmoduleNested(cmFortranParser* parser,
                                         const char* module_name,
                                         const char* submodule_name,
                                         const char* nested_submodule_name)
{
  if (parser->InPPFalseBranch) {
    return;
  }

  // syntax:   "submodule (module_name:submodule_name) nested_submodule_name"
  // requires: "module_name@submodule_name.smod"
  // provides: "module_name@nested_submodule_name.smod"

  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
  std::string const& sub_name = cmSystemTools::LowerCase(submodule_name);
  std::string const& nest_name =
    cmSystemTools::LowerCase(nested_submodule_name);
308 309
  parser->Info.Requires.insert(parser->SModName(mod_name, sub_name));
  parser->Info.Provides.insert(parser->SModName(mod_name, nest_name));
310 311
}

312
void cmFortranParser_RuleDefine(cmFortranParser* parser, const char* macro)
313
{
314
  if (!parser->InPPFalseBranch) {
315
    parser->PPDefinitions.insert(macro);
316
  }
317 318
}

319
void cmFortranParser_RuleUndef(cmFortranParser* parser, const char* macro)
320
{
321
  if (!parser->InPPFalseBranch) {
322 323
    std::set<std::string>::iterator match;
    match = parser->PPDefinitions.find(macro);
324
    if (match != parser->PPDefinitions.end()) {
325 326
      parser->PPDefinitions.erase(match);
    }
327
  }
328 329
}

330
void cmFortranParser_RuleIfdef(cmFortranParser* parser, const char* macro)
331 332 333 334
{
  // A new PP branch has been opened
  parser->SkipToEnd.push(false);

335
  if (parser->InPPFalseBranch) {
336
    parser->InPPFalseBranch++;
337 338 339 340
  } else if (parser->PPDefinitions.find(macro) ==
             parser->PPDefinitions.end()) {
    parser->InPPFalseBranch = 1;
  } else {
341
    parser->SkipToEnd.top() = true;
342
  }
343 344
}

345
void cmFortranParser_RuleIfndef(cmFortranParser* parser, const char* macro)
346 347 348 349
{
  // A new PP branch has been opened
  parser->SkipToEnd.push(false);

350
  if (parser->InPPFalseBranch) {
351
    parser->InPPFalseBranch++;
352 353
  } else if (parser->PPDefinitions.find(macro) !=
             parser->PPDefinitions.end()) {
354
    parser->InPPFalseBranch = 1;
355
  } else {
356 357
    // ignore other branches
    parser->SkipToEnd.top() = true;
358
  }
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
}

void cmFortranParser_RuleIf(cmFortranParser* parser)
{
  /* Note: The current parser is _not_ able to get statements like
   *   #if 0
   *   #if 1
   *   #if MYSMBOL
   *   #if defined(MYSYMBOL)
   *   #if defined(MYSYMBOL) && ...
   * right.  The same for #elif.  Thus in
   *   #if SYMBOL_1
   *     ..
   *   #elif SYMBOL_2
   *     ...
   *     ...
   *   #elif SYMBOL_N
   *     ..
   *   #else
   *     ..
   *   #endif
   * _all_ N+1 branches are considered.  If you got something like this
   *   #if defined(MYSYMBOL)
   *   #if !defined(MYSYMBOL)
   * use
   *   #ifdef MYSYMBOL
   *   #ifndef MYSYMBOL
   * instead.
   */

  // A new PP branch has been opened
  // Never skip!  See note above.
  parser->SkipToEnd.push(false);
}

void cmFortranParser_RuleElif(cmFortranParser* parser)
{
  /* Note: There are parser limitations.  See the note at
   * cmFortranParser_RuleIf(..)
   */

  // Always taken unless an #ifdef or #ifndef-branch has been taken
  // already.  If the second condition isn't meet already
  // (parser->InPPFalseBranch == 0) correct it.
403 404
  if (!parser->SkipToEnd.empty() && parser->SkipToEnd.top() &&
      !parser->InPPFalseBranch) {
405
    parser->InPPFalseBranch = 1;
406
  }
407 408 409 410 411
}

void cmFortranParser_RuleElse(cmFortranParser* parser)
{
  // if the parent branch is false do nothing!
412
  if (parser->InPPFalseBranch > 1) {
413
    return;
414
  }
415 416 417

  // parser->InPPFalseBranch is either 0 or 1.  We change it depending on
  // parser->SkipToEnd.top()
418
  if (!parser->SkipToEnd.empty() && parser->SkipToEnd.top()) {
419
    parser->InPPFalseBranch = 1;
420
  } else {
421
    parser->InPPFalseBranch = 0;
422
  }
423 424 425 426
}

void cmFortranParser_RuleEndif(cmFortranParser* parser)
{
427
  if (!parser->SkipToEnd.empty()) {
428
    parser->SkipToEnd.pop();
429
  }
430 431 432

  // #endif doesn't know if there was a "#else" in before, so it
  // always decreases InPPFalseBranch
433
  if (parser->InPPFalseBranch) {
434
    parser->InPPFalseBranch--;
435
  }
436
}