class UnparseVisitor {
public:
UnparseVisitor(std::ostream &out, int indentationAmount, Encoding encoding,
- bool capitalize)
+ bool capitalize, preStatementType *preStatement)
: out_{out}, indentationAmount_{indentationAmount}, encoding_{encoding},
- capitalizeKeywords_{capitalize} {}
+ capitalizeKeywords_{capitalize}, preStatement_{preStatement} {}
// In nearly all cases, this code avoids defining Boolean-valued Pre()
// callbacks for the parse tree walking framework in favor of two void
// Statement labels and ends of lines
template<typename T> void Before(const Statement<T> &x) {
+ if (preStatement_) {
+ (*preStatement_)(x.source, out_, indent_);
+ }
Walk(x.label, " ");
}
template<typename T> void Post(const Statement<T> &) { Put('\n'); }
std::set<CharBlock> structureComponents_;
Encoding encoding_{Encoding::UTF8};
bool capitalizeKeywords_{true};
+ preStatementType *preStatement_{nullptr};
};
void UnparseVisitor::Put(char ch) {
void UnparseVisitor::Word(const std::string &str) { Word(str.c_str()); }
void Unparse(std::ostream &out, const Program &program, Encoding encoding,
- bool capitalizeKeywords) {
- UnparseVisitor visitor{out, 1, encoding, capitalizeKeywords};
+ bool capitalizeKeywords, preStatementType *preStatement) {
+ UnparseVisitor visitor{out, 1, encoding, capitalizeKeywords, preStatement};
Walk(program, visitor);
visitor.Done();
}
#ifndef FORTRAN_PARSER_UNPARSE_H_
#define FORTRAN_PARSER_UNPARSE_H_
+#include "char-block.h"
#include "characters.h"
+#include <functional>
#include <iosfwd>
namespace Fortran::parser {
struct Program;
+// A function called before each Statement is unparsed.
+using preStatementType =
+ std::function<void(const CharBlock &, std::ostream &, int)>;
+
/// Convert parsed program to out as Fortran.
void Unparse(std::ostream &out, const Program &program,
- Encoding encoding = Encoding::UTF8, bool capitalizeKeywords = true);
+ Encoding encoding = Encoding::UTF8, bool capitalizeKeywords = true,
+ preStatementType *preStatement = nullptr);
} // namespace Fortran::parser
scope.cc
symbol.cc
type.cc
+ unparse-with-symbols.cc
)
target_link_libraries(FlangSemantics
EnumToString(attr), name.source);
}
symbol.attrs().set(attr);
+ symbol.add_occurrence(name.source);
}
}
return false;
void DeclarationVisitor::SetType(
const SourceName &name, Symbol &symbol, const DeclTypeSpec &type) {
- if (auto *details = symbol.detailsIf<EntityDetails>()) {
- if (!details->type()) {
- details->set_type(type);
- return;
- }
- } else if (auto *details = symbol.detailsIf<ObjectEntityDetails>()) {
- if (!details->type()) {
- details->set_type(type);
- return;
- }
- } else if (auto *details = symbol.detailsIf<ProcEntityDetails>()) {
- if (!details->interface().type()) {
- details->interface().set_type(type);
- return;
- }
- } else {
+ if (symbol.GetType()) {
+ Say(name, "The type of '%s' has already been declared"_err_en_US);
return;
}
- Say(name, "The type of '%s' has already been declared"_err_en_US);
+ symbol.SetType(type);
}
// ResolveNamesVisitor implementation
"Declaration of '%s'"_en_US, typeName.ToString().data()});
return nullptr;
}
- return it->second;
+ auto *symbol{it->second};
+ symbol->add_occurrence(component);
+ return symbol;
}
void ResolveNamesVisitor::Post(const parser::ProcedureDesignator &x) {
}
}
+const DeclTypeSpec *Symbol::GetType() const {
+ return std::visit(
+ common::visitors{
+ [](const EntityDetails &x) {
+ return x.type().has_value() ? &x.type().value() : nullptr;
+ },
+ [](const ObjectEntityDetails &x) {
+ return x.type().has_value() ? &x.type().value() : nullptr;
+ },
+ [](const ProcEntityDetails &x) { return x.interface().type(); },
+ [](const auto &) {
+ return static_cast<const DeclTypeSpec *>(nullptr);
+ },
+ },
+ details_);
+}
+
+void Symbol::SetType(const DeclTypeSpec &type) {
+ std::visit(
+ common::visitors{
+ [&](EntityDetails &x) { x.set_type(type); },
+ [&](ObjectEntityDetails &x) { x.set_type(type); },
+ [&](ProcEntityDetails &x) { x.interface().set_type(type); },
+ [](auto &) {},
+ },
+ details_);
+}
+
bool Symbol::isSubprogram() const {
return std::visit(
common::visitors{
}
static std::ostream &DumpType(std::ostream &os, const Symbol &symbol) {
- if (const auto *details = symbol.detailsIf<EntityDetails>()) {
- if (details->type()) {
- os << *details->type() << ' ';
- }
+ if (const auto *type{symbol.GetType()}) {
+ os << *type << ' ';
}
return os;
}
return os;
}
+// Output a unique name for a scope by qualifying it with the names of
+// parent scopes. For scopes without corresponding symbols, use "ANON".
+static void DumpUniqueName(std::ostream &os, const Scope &scope) {
+ if (&scope != &Scope::globalScope) {
+ DumpUniqueName(os, scope.parent());
+ os << '/';
+ if (auto *scopeSymbol{scope.symbol()}) {
+ os << scopeSymbol->name().ToString();
+ } else {
+ os << "ANON";
+ }
+ }
+}
+
+// Dump a symbol for UnparseWithSymbols. This will be used for tests so the
+// format should be reasonably stable.
+std::ostream &DumpForUnparse(
+ std::ostream &os, const Symbol &symbol, bool isDef) {
+ DumpUniqueName(os, symbol.owner());
+ os << '/' << symbol.name().ToString();
+ if (isDef) {
+ if (!symbol.attrs().empty()) {
+ os << ' ' << symbol.attrs();
+ }
+ if (symbol.test(Symbol::Flag::Implicit)) {
+ os << " (implicit)";
+ }
+ os << ' ' << symbol.GetDetailsName();
+ if (const auto *type{symbol.GetType()}) {
+ os << ' ' << *type;
+ }
+ }
+ return os;
+}
+
} // namespace Fortran::semantics
Symbol &GetUltimate();
const Symbol &GetUltimate() const;
+ const DeclTypeSpec *GetType() const;
+ void SetType(const DeclTypeSpec &);
+
bool isSubprogram() const;
bool HasExplicitInterface() const;
Symbol() {} // only created in class Symbols
const std::string GetDetailsName() const;
friend std::ostream &operator<<(std::ostream &, const Symbol &);
+ friend std::ostream &DumpForUnparse(std::ostream &, const Symbol &, bool);
template<std::size_t> friend class Symbols;
template<class, std::size_t> friend struct std::array;
};
--- /dev/null
+// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "unparse-with-symbols.h"
+#include "symbol.h"
+#include "../parser/parse-tree-visitor.h"
+#include "../parser/parse-tree.h"
+#include "../parser/unparse.h"
+#include <map>
+#include <ostream>
+#include <set>
+
+namespace Fortran::semantics {
+
+// Walk the parse tree and collection information about which statements
+// define and reference symbols. Then PrintSymbols outputs information
+// by statement.
+class SymbolDumpVisitor {
+public:
+ // Write out symbols defined or referenced at this statement.
+ void PrintSymbols(const parser::CharBlock &stmt, std::ostream &, int) const;
+
+ template<typename T> bool Pre(const T &) { return true; }
+ template<typename T> void Post(const T &) {}
+ template<typename T> bool Pre(const parser::Statement<T> &stmt) {
+ currStmt_ = &stmt.source;
+ return true;
+ }
+ template<typename T> void Post(const parser::Statement<T> &) {
+ currStmt_ = nullptr;
+ }
+ void Post(const parser::Name &);
+ void Post(const parser::EndFunctionStmt &) { EndScope(); }
+ void Post(const parser::EndModuleStmt &) { EndScope(); }
+ void Post(const parser::EndMpSubprogramStmt &) { EndScope(); }
+ void Post(const parser::EndProgramStmt &) { EndScope(); }
+ void Post(const parser::EndSubmoduleStmt &) { EndScope(); }
+ void Post(const parser::EndSubroutineStmt &) { EndScope(); }
+ bool Pre(const parser::DataRef &) { return PreReference(); }
+ void Post(const parser::DataRef &) { PostReference(); }
+ bool Pre(const parser::Designator &) { return PreReference(); }
+ void Post(const parser::Designator &) { PostReference(); }
+ bool Pre(const parser::StructureComponent &) { return PreReference(); }
+ void Post(const parser::StructureComponent &) { PostReference(); }
+ bool Pre(const parser::ProcedureDesignator &) { return PreReference(); }
+ void Post(const parser::ProcedureDesignator &) { PostReference(); }
+ bool Pre(const parser::DerivedTypeSpec &) { return PreReference(); }
+ void Post(const parser::DerivedTypeSpec &) { PostReference(); }
+ bool Pre(const parser::UseStmt &) { return PreReference(); }
+ void Post(const parser::UseStmt &) { PostReference(); }
+
+private:
+ using symbolMap = std::multimap<const char *, const Symbol *>;
+
+ const SourceName *currStmt_{nullptr}; // current statement we are processing
+ int isRef_{0}; // > 0 means in the context of a reference
+ symbolMap defs_; // statement location to symbol defined there
+ symbolMap refs_; // statement location to symbol referenced there
+ std::map<const Symbol *, const char *> symbolToStmt_; // symbol to def
+
+ void EndScope();
+ bool PreReference();
+ void PostReference();
+ bool isRef() const { return isRef_ > 0; }
+ void PrintSymbols(const parser::CharBlock &, std::ostream &, int,
+ const symbolMap &, bool) const;
+ void Indent(std::ostream &, int) const;
+};
+
+void SymbolDumpVisitor::PrintSymbols(
+ const parser::CharBlock &location, std::ostream &out, int indent) const {
+ PrintSymbols(location, out, indent, defs_, true);
+ PrintSymbols(location, out, indent, refs_, false);
+}
+void SymbolDumpVisitor::PrintSymbols(const parser::CharBlock &location,
+ std::ostream &out, int indent, const symbolMap &symbols, bool isDef) const {
+ std::set<const Symbol *> done; // used to prevent duplicates
+ auto range{symbols.equal_range(location.begin())};
+ for (auto it{range.first}; it != range.second; ++it) {
+ auto *symbol{it->second};
+ if (done.insert(symbol).second) {
+ Indent(out, indent);
+ out << '!' << (isDef ? "DEF"s : "REF"s) << ": ";
+ DumpForUnparse(out, symbol->GetUltimate(), isDef);
+ out << '\n';
+ }
+ }
+}
+void SymbolDumpVisitor::Indent(std::ostream &out, int indent) const {
+ for (int i{0}; i < indent; ++i) {
+ out << ' ';
+ }
+}
+
+void SymbolDumpVisitor::Post(const parser::Name &name) {
+ if (const auto *symbol{name.symbol}) {
+ CHECK(currStmt_);
+ // If this is the first reference to an implicitly defined symbol,
+ // record it as a def.
+ bool isImplicit{symbol->test(Symbol::Flag::Implicit) &&
+ symbolToStmt_.find(symbol) == symbolToStmt_.end()};
+ if (isRef() && !isImplicit) {
+ refs_.emplace(currStmt_->begin(), symbol);
+ } else {
+ symbolToStmt_.emplace(symbol, currStmt_->begin());
+ }
+ }
+}
+
+// Defs are initially saved in symbolToStmt_ so that a symbol defined across
+// multiple statements is associated with only one (the first). Now that we
+// are at the end of a scope, move them into defs_.
+void SymbolDumpVisitor::EndScope() {
+ for (auto pair : symbolToStmt_) {
+ defs_.emplace(pair.second, pair.first);
+ }
+ symbolToStmt_.clear();
+}
+
+// {Pre,Post}Reference() are called around constructs that contains symbols
+// references. Sometimes those are nested (e.g. DataRef inside Designator)
+// so we need to maintain a count to know when we are back out.
+bool SymbolDumpVisitor::PreReference() {
+ ++isRef_;
+ return true;
+}
+void SymbolDumpVisitor::PostReference() {
+ CHECK(isRef_ > 0);
+ --isRef_;
+}
+
+void UnparseWithSymbols(std::ostream &out, const parser::Program &program,
+ parser::Encoding encoding) {
+ SymbolDumpVisitor visitor;
+ parser::Walk(program, visitor);
+ parser::preStatementType preStatement{
+ [&](const parser::CharBlock &location, std::ostream &out, int indent) {
+ visitor.PrintSymbols(location, out, indent);
+ }};
+ parser::Unparse(out, program, encoding, false, &preStatement);
+}
+
+} // namespace Fortran::semantics
--- /dev/null
+// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef FORTRAN_SEMANTICS_UNPARSE_WITH_SYMBOLS_H_
+#define FORTRAN_SEMANTICS_UNPARSE_WITH_SYMBOLS_H_
+
+#include "../parser/characters.h"
+#include <iosfwd>
+
+namespace Fortran::parser {
+struct Program;
+} // namespace Fortran::parser
+
+namespace Fortran::semantics {
+void UnparseWithSymbols(std::ostream &, const parser::Program &,
+ parser::Encoding encoding = parser::Encoding::UTF8);
+} // namespace Fortran::semantics
+
+#endif // FORTRAN_SEMANTICS_UNPARSE_WITH_SYMBOLS_H_
#include "../../lib/parser/unparse.h"
#include "../../lib/semantics/dump-parse-tree.h"
#include "../../lib/semantics/resolve-names.h"
+#include "../../lib/semantics/unparse-with-symbols.h"
#include <cerrno>
#include <cstdio>
#include <cstring>
exitStatus = EXIT_FAILURE;
return {};
}
+ auto &parseTree{*parsing.parseTree()};
if (driver.measureTree) {
- MeasureParseTree(*parsing.parseTree());
- }
- if (driver.debugResolveNames || driver.dumpSymbols) {
- Fortran::semantics::ResolveNames(*parsing.parseTree(), parsing.cooked());
- if (driver.dumpSymbols) {
- Fortran::semantics::DumpSymbols(std::cout);
- }
+ MeasureParseTree(parseTree);
}
if (driver.dumpParseTree) {
- Fortran::semantics::DumpTree(std::cout, *parsing.parseTree());
+ Fortran::semantics::DumpTree(std::cout, parseTree);
}
if (driver.dumpUnparse) {
- Unparse(
- std::cout, *parsing.parseTree(), driver.encoding, true /*capitalize*/);
+ if (driver.dumpSymbols) {
+ Fortran::semantics::ResolveNames(parseTree, parsing.cooked());
+ Fortran::semantics::UnparseWithSymbols(
+ std::cout, parseTree, driver.encoding);
+ } else {
+ Unparse(std::cout, parseTree, driver.encoding, true);
+ }
return {};
}
+ if (driver.debugResolveNames || driver.dumpSymbols) {
+ Fortran::semantics::ResolveNames(parseTree, parsing.cooked());
+ if (driver.dumpSymbols) {
+ Fortran::semantics::DumpSymbols(std::cout);
+ }
+ }
if (driver.parseOnly) {
return {};
}
{
std::ofstream tmpSource;
tmpSource.open(tmpSourcePath);
- Unparse(tmpSource, *parsing.parseTree(), driver.encoding);
+ Unparse(tmpSource, parseTree, driver.encoding);
}
if (ParentProcess()) {