* @file
*
* This file implements the symbol table printing function.
*/
#include "PrintSymbolTable.h"
#include "cangjie/AST/Match.h"
#include "cangjie/AST/Symbol.h"
#include "cangjie/Basic/Print.h"
using namespace Cangjie;
using namespace AST;
namespace {
constexpr unsigned int ONE_INDENT = 1;
constexpr unsigned int TWO_INDENT = 2;
constexpr unsigned int THREE_INDENT = 3;
constexpr unsigned int FOUR_INDENT = 4;
constexpr unsigned int FIVE_INDENT = 5;
template <typename... Args> inline void PrintIndentNoSplitNoEndl(const unsigned int indent, const Args... args)
{
PrintIndentOnly(indent);
PrintNoSplit(args...);
}
template <typename... Args> inline void PrintIndentNoSplit(const unsigned int indent, const Args... args)
{
PrintIndentNoSplitNoEndl(indent, args...);
Println();
}
void PrintPackage(const Symbol& symbol)
{
PrintIndent(TWO_INDENT, "{");
PrintIndentNoSplit(THREE_INDENT, "\"name\": \"", symbol.name, "\",");
PrintIndent(THREE_INDENT, "\"files\": [");
Ptr<Node> node = symbol.node;
CJC_NULLPTR_CHECK(node);
CJC_ASSERT(node->astKind == ASTKind::PACKAGE);
Ptr<Package> package = StaticAs<ASTKind::PACKAGE>(node);
auto iter = package->files.cbegin();
PrintIndentNoSplitNoEndl(FOUR_INDENT, "\"", (*iter)->filePath, "\"");
++iter;
std::for_each(iter, package->files.cend(), [](auto& file) {
Println(",");
PrintIndentNoSplitNoEndl(FOUR_INDENT, "\"", file->filePath, "\"");
});
Println();
PrintIndent(THREE_INDENT, "]");
PrintIndentNoSplitNoEndl(TWO_INDENT, "}");
}
void PrintPackages(const std::vector<std::reference_wrapper<const Symbol>>& packages)
{
if (packages.empty()) {
return;
}
std::unordered_set<std::string> packageNames;
auto iter = packages.cbegin();
packageNames.emplace((*iter).get().name);
PrintPackage(*iter);
++iter;
std::for_each(iter, packages.cend(), [&packageNames](const Symbol& sym) {
if (packageNames.emplace(sym.name).second) {
Println(",");
PrintPackage(sym);
}
});
Println();
}
void PrintPosition(unsigned int indent, const Position& pos, const std::string& name, bool hasComma)
{
PrintIndentNoSplit(indent, "\"", name, "\": {");
PrintIndentNoSplit(indent + ONE_INDENT, "\"line\": ", pos.line, ",");
PrintIndent(indent + ONE_INDENT, "\"column\":", pos.column);
PrintIndentNoSplit(indent, "}", hasComma ? "," : "");
}
void PrintPackageSpecPart(const PackageSpec& packageSpec)
{
PrintIndentNoSplit(FIVE_INDENT, "\"packageName\": \"", packageSpec.packageName.Val(), "\",");
PrintPosition(FIVE_INDENT, packageSpec.macroPos, "macroPos", true);
PrintPosition(FIVE_INDENT, packageSpec.packagePos, "packagePos", true);
PrintPosition(FIVE_INDENT, packageSpec.packageName.Begin(), "packageNamePos", true);
}
void PrintImportSpecPart(const ImportSpec& importSpec)
{
PrintPosition(FIVE_INDENT, importSpec.importPos, "importPos", true);
PrintIndentNoSplit(FIVE_INDENT, "\"packageName\": \"", GetImportedItemFullName(importSpec.content), "\",");
PrintPosition(FIVE_INDENT, importSpec.content.begin, "packageNamePos", true);
if (importSpec.IsImportAlias()) {
PrintPosition(FIVE_INDENT, importSpec.content.asPos, "asPos", true);
PrintIndentNoSplit(FIVE_INDENT, "\"asIdentifier\": \"", importSpec.content.aliasName.Val(), "\",");
PrintPosition(FIVE_INDENT, importSpec.content.aliasName.Begin(), "asIdentifierPos", true);
}
}
void PrintDeclPart(const Decl& decl)
{
PrintIndentNoSplit(FIVE_INDENT, "\"identifier\": \"", decl.identifier.Val(), "\",");
PrintPosition(FIVE_INDENT, decl.identifier.Begin(), "identifierPos", true);
}
void PrintNode(const Node& node)
{
if (node.TestAttr(Attribute::COMPILER_ADD)) {
return;
}
PrintIndent(FOUR_INDENT, "{");
PrintIndentNoSplit(FIVE_INDENT, "\"astKind\": \"", ASTKIND_TO_STRING_MAP[node.astKind], "\",");
PrintIndentNoSplit(FIVE_INDENT, "\"name\": \"", node.symbol ? node.symbol->name : "", "\",");
if (auto packageSpec = DynamicCast<const PackageSpec*>(&node); packageSpec) {
PrintPackageSpecPart(*packageSpec);
} else if (auto importSpec = DynamicCast<const ImportSpec*>(&node); importSpec) {
PrintImportSpecPart(*importSpec);
} else if (auto decl = DynamicCast<const Decl*>(&node); decl) {
PrintDeclPart(*decl);
}
PrintPosition(FIVE_INDENT, node.begin, "begin", true);
PrintPosition(FIVE_INDENT, node.end, "end", false);
PrintIndentNoSplitNoEndl(FOUR_INDENT, "}");
}
void PrintFile(const File& file, const std::vector<std::reference_wrapper<const Node>>& nodes)
{
PrintIndent(TWO_INDENT, "{");
PrintIndentNoSplit(THREE_INDENT, "\"path\": \"", file.filePath, "\",");
PrintIndentNoSplit(THREE_INDENT, "\"symbols\": [");
if (!nodes.empty()) {
auto iter = nodes.cbegin();
PrintNode(*iter);
++iter;
std::for_each(iter, nodes.cend(), [](const Node& node) {
Println(",");
PrintNode(node);
});
Println();
}
PrintIndentNoSplit(THREE_INDENT, "]");
PrintIndentNoSplitNoEndl(TWO_INDENT, "}");
}
void PrintFiles(
const std::unordered_map<Ptr<const File>, std::vector<std::reference_wrapper<const Node>>>& file2nodesMap)
{
if (file2nodesMap.empty()) {
return;
}
auto iter = file2nodesMap.cbegin();
PrintFile(*(*iter).first, (*iter).second);
++iter;
std::for_each(iter, file2nodesMap.cend(),
[](const std::pair<Ptr<const File>, std::vector<std::reference_wrapper<const Node>>>& pair) {
Println(",");
PrintFile(*pair.first, pair.second);
});
Println();
}
void HandleFileNode(std::unordered_map<Ptr<const File>, std::vector<std::reference_wrapper<const Node>>>& file2nodesMap,
const Symbol& symbol, const Node& node)
{
if (node.astKind != ASTKind::FILE) {
return;
}
if (&symbol == node.symbol) {
return;
}
const File& file = static_cast<const File&>(node);
std::vector<std::reference_wrapper<const Node>>& nodes =
file2nodesMap.emplace(std::piecewise_construct, std::make_tuple(&file), std::make_tuple()).first->second;
if (file.package) {
nodes.push_back(*file.package);
}
for (auto& import : file.imports) {
if (import->importPos.line != 0) {
nodes.push_back(*import);
}
}
}
void AddSymbolToInfo(std::vector<std::reference_wrapper<const Symbol>>& packages,
std::unordered_map<Ptr<const File>, std::vector<std::reference_wrapper<const Node>>>& file2nodesMap,
std::optional<std::reference_wrapper<File>>& fileOpt, const Symbol& symbol)
{
CJC_NULLPTR_CHECK(symbol.node);
Node& node = *symbol.node;
if (node.TestAttr(Attribute::COMPILER_ADD)) {
return;
}
if (node.astKind == ASTKind::PACKAGE) {
packages.push_back(symbol);
} else {
if (node.astKind == ASTKind::FILE) {
fileOpt = static_cast<File&>(node);
}
CJC_ASSERT(fileOpt);
file2nodesMap.emplace(std::piecewise_construct, std::make_tuple(&fileOpt->get()), std::make_tuple())
.first->second.push_back(node);
HandleFileNode(file2nodesMap, symbol, node);
}
}
}
void Cangjie::PrintSymbolTable(CompilerInstance& ci)
{
std::vector<std::reference_wrapper<const Symbol>> packages;
std::unordered_map<Ptr<const File>, std::vector<std::reference_wrapper<const Node>>> file2nodesMap;
for (auto srcPkg : ci.GetSourcePackages()) {
ASTContext* ctx = ci.GetASTContextByPackage(srcPkg);
CJC_NULLPTR_CHECK(ctx);
std::optional<std::reference_wrapper<File>> fileOpt;
for (auto& sym : std::as_const(ctx->symbolTable)) {
AddSymbolToInfo(packages, file2nodesMap, fileOpt, *sym);
}
}
Println("{");
PrintIndent(ONE_INDENT, "\"packages\": [");
PrintPackages(packages);
PrintIndent(ONE_INDENT, "],");
PrintIndent(ONE_INDENT, "\"files\": [");
PrintFiles(file2nodesMap);
PrintIndent(ONE_INDENT, "]");
Println("}");
}