// slang-ast-print.cpp #include "slang-ast-print.h" #include "slang-check-impl.h" namespace Slang { ASTPrinter::Part::Kind ASTPrinter::Part::getKind(ASTPrinter::Part::Type type) { typedef ASTPrinter::Part::Kind Kind; typedef ASTPrinter::Part::Type Type; switch (type) { case Type::ParamType: return Kind::Type; case Type::ParamName: return Kind::Name; case Type::ReturnType: return Kind::Type; case Type::DeclPath: return Kind::Name; case Type::GenericParamType: return Kind::Type; case Type::GenericParamValue: return Kind::Value; case Type::GenericParamValueType: return Kind::Type; default: break; } return Kind::None; } void ASTPrinter::addType(Type* type) { type->toText(m_builder); } void ASTPrinter::addVal(Val* val) { val->toText(m_builder); } /* static */void ASTPrinter::appendDeclName(Decl* decl, StringBuilder& out) { if (as(decl)) { out << "init"; } else if (as(decl)) { out << "subscript"; } else { out << getText(decl->getName()); } } void ASTPrinter::_addDeclName(Decl* decl) { appendDeclName(decl, m_builder); } void ASTPrinter::addOverridableDeclPath(const DeclRef& declRef) { ScopePart scopePart(this, Part::Type::DeclPath); _addDeclPathRec(declRef, 0); } void ASTPrinter::addDeclPath(const DeclRef& declRef) { ScopePart scopePart(this, Part::Type::DeclPath); _addDeclPathRec(declRef, 1); } void ASTPrinter::_addDeclPathRec(const DeclRef& declRef, Index depth) { auto& sb = m_builder; // Find the parent declaration auto parentDeclRef = declRef.getParent(); // If the immediate parent is a generic, then we probably // want the declaration above that... auto parentGenericDeclRef = parentDeclRef.as(); if (parentGenericDeclRef) { parentDeclRef = parentGenericDeclRef.getParent(); } // Depending on what the parent is, we may want to format things specially if (auto aggTypeDeclRef = parentDeclRef.as()) { _addDeclPathRec(aggTypeDeclRef, depth + 1); sb << toSlice("."); } else if (auto namespaceDeclRef = parentDeclRef.as()) { _addDeclPathRec(namespaceDeclRef, depth + 1); // Hmm, it could be argued that we follow the . as seen in AggType as is followed in some other languages // like Java. // That it is useful to have a distinction between something that is a member/method and something that is // in a scope (such as a namespace), and is something that has returned to later languages probably for that // reason (Slang accepts . or ::). So for now this is follows the :: convention. // // It could be argued them that the previous '.' use should vary depending on that distinction. sb << toSlice("::"); } else if (auto extensionDeclRef = parentDeclRef.as()) { ExtensionDecl* extensionDecl = as(parentDeclRef.getDecl()); Type* type = extensionDecl->targetType.type; addType(type); sb << toSlice("."); } else if (auto moduleDecl = as(parentDeclRef.getDecl())) { Name* moduleName = moduleDecl->getName(); if ((m_optionFlags & OptionFlag::ModuleName) && moduleName) { // Use to say in modules scope sb << moduleName->text << toSlice("::"); } } // If this decl is the module, we only output it's name if that feature is enabled if (ModuleDecl* moduleDecl = as(declRef.getDecl())) { Name* moduleName = moduleDecl->getName(); if ((m_optionFlags & OptionFlag::ModuleName) && moduleName) { sb << moduleName->text; } return; } _addDeclName(declRef.getDecl()); // If the parent declaration is a generic, then we need to print out its // signature if (parentGenericDeclRef) { auto genSubst = as(declRef.substitutions.substitutions); if (genSubst) { SLANG_RELEASE_ASSERT(genSubst); SLANG_RELEASE_ASSERT(genSubst->genericDecl == parentGenericDeclRef.getDecl()); // If the name we printed previously was an operator // that ends with `<`, then immediately printing the // generic arguments inside `<...>` may cause it to // be hard to parse the operator name visually. // // We thus include a space between the declaration name // and its generic arguments in this case. // if (sb.endsWith("<")) { sb << " "; } sb << "<"; bool first = true; for (auto arg : genSubst->args) { // When printing the representation of a specialized // generic declaration we don't want to include the // argument values for subtype witnesses since these // do not correspond to parameters of the generic // as the user sees it. // if (as(arg)) continue; if (!first) sb << ", "; addVal(arg); first = false; } sb << ">"; } else if (depth > 0) { // Write out the generic parameters (only if the depth allows it) addGenericParams(parentGenericDeclRef); } } } void ASTPrinter::addGenericParams(const DeclRef& genericDeclRef) { auto& sb = m_builder; sb << "<"; bool first = true; for (auto paramDeclRef : getMembers(genericDeclRef)) { if (auto genericTypeParam = paramDeclRef.as()) { if (!first) sb << ", "; first = false; { ScopePart scopePart(this, Part::Type::GenericParamType); sb << getText(genericTypeParam.getName()); } } else if (auto genericValParam = paramDeclRef.as()) { if (!first) sb << ", "; first = false; { ScopePart scopePart(this, Part::Type::GenericParamValue); sb << getText(genericValParam.getName()); } sb << ":"; { ScopePart scopePart(this, Part::Type::GenericParamValueType); addType(getType(m_astBuilder, genericValParam)); } } else { } } sb << ">"; } void ASTPrinter::addDeclParams(const DeclRef& declRef) { auto& sb = m_builder; if (auto funcDeclRef = declRef.as()) { // This is something callable, so we need to also print parameter types for overloading sb << "("; bool first = true; for (auto paramDeclRef : getParameters(funcDeclRef)) { if (!first) sb << ", "; ParamDecl* paramDecl = paramDeclRef; { ScopePart scopePart(this, Part::Type::ParamType); // Seems these apply to parameters/VarDeclBase and are not part of the 'type' // but seems more appropriate to put in the Type Part if (paramDecl->hasModifier()) { sb << toSlice("inout "); } else if (paramDecl->hasModifier()) { sb << toSlice("out "); } else if (paramDecl->hasModifier()) { sb << toSlice("in "); } // And this to params/variables (not the type) if (paramDecl->hasModifier()) { sb << toSlice("const "); } addType(getType(m_astBuilder, paramDeclRef)); } // Output the parameter name if there is one, and it's enabled in the options if (m_optionFlags & OptionFlag::ParamNames && paramDecl->getName()) { sb << " "; { ScopePart scopePart(this, Part::Type::ParamName); sb << paramDecl->getName()->text; } } first = false; } sb << ")"; } else if (auto genericDeclRef = declRef.as()) { addGenericParams(genericDeclRef); addDeclParams(DeclRef(getInner(genericDeclRef), genericDeclRef.substitutions)); } else { } } void ASTPrinter::addDeclKindPrefix(Decl* decl) { if (auto genericDecl = as(decl)) { decl = genericDecl->inner; } if (as(decl)) { m_builder << "func "; } } void ASTPrinter::addDeclResultType(const DeclRef& inDeclRef) { DeclRef declRef = inDeclRef; if (auto genericDeclRef = declRef.as()) { declRef = DeclRef(getInner(genericDeclRef), genericDeclRef.substitutions); } if (as(declRef)) { } else if (auto callableDeclRef = declRef.as()) { m_builder << " -> "; { ScopePart scopePart(this, Part::Type::ReturnType); addType(getResultType(m_astBuilder, callableDeclRef)); } } } /* static */void ASTPrinter::addDeclSignature(const DeclRef& declRef) { addDeclKindPrefix(declRef.getDecl()); addDeclPath(declRef); addDeclParams(declRef); addDeclResultType(declRef); } /* static */String ASTPrinter::getDeclSignatureString(DeclRef declRef, ASTBuilder* astBuilder) { ASTPrinter astPrinter(astBuilder); astPrinter.addDeclSignature(declRef); return astPrinter.getString(); } /* static */String ASTPrinter::getDeclSignatureString(const LookupResultItem& item, ASTBuilder* astBuilder) { return getDeclSignatureString(item.declRef, astBuilder); } /* static */UnownedStringSlice ASTPrinter::getPart(Part::Type partType, const UnownedStringSlice& slice, const List& parts) { const Index index = parts.findFirstIndex([&](const Part& part) -> bool { return part.type == partType; }); return index >= 0 ? getPart(slice, parts[index]) : UnownedStringSlice(); } UnownedStringSlice ASTPrinter::getPartSlice(Part::Type partType) const { return m_parts ? getPart(partType, getSlice(), *m_parts) : UnownedStringSlice(); } } // namespace Slang