// slang-ast-print.cpp #include "slang-ast-print.h" #include "core/slang-char-util.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) { if (!type) { m_builder << ""; return; } type = type->getCanonicalType(); if (m_optionFlags & OptionFlag::SimplifiedBuiltinType) { if (auto vectorType = as(type)) { if (as(vectorType->getElementType())) { vectorType->getElementType()->toText(m_builder); if (as(vectorType->getElementCount())) { m_builder << vectorType->getElementCount(); return; } } } else if (auto matrixType = as(type)) { auto elementType = matrixType->getElementType(); if (as(elementType)) { matrixType->getElementType()->toText(m_builder); if (as(matrixType->getRowCount()) && as(matrixType->getColumnCount())) { m_builder << matrixType->getRowCount() << "x" << matrixType->getColumnCount(); return; } } } } 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 { auto text = getText(decl->getName()); if (text.getLength() && !(CharUtil::isAlphaOrDigit(text[0]) || text[0] == '_')) out << "operator" << text; else out << text; } } 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; if (m_optionFlags & OptionFlag::NoSpecializedExtensionTypeName) { if (auto unspecializedDeclRef = isDeclRefTypeOf(type)) { type = DeclRefType::create( m_astBuilder, unspecializedDeclRef.getDecl()->getDefaultDeclRef()); } } 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 && !declRef.as() && !declRef.as()) { auto substArgs = tryGetGenericArguments(SubstitutionSet(declRef), parentGenericDeclRef.getDecl()); if (substArgs.getCount()) { // 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 : substArgs) { // 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(m_astBuilder, 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 if (auto genericTypePackParam = paramDeclRef.as()) { if (!first) sb << ", "; first = false; { ScopePart scopePart(this, Part::Type::GenericParamType); sb << "each "; sb << getText(genericTypePackParam.getName()); } } else { } } sb << ">"; } void ASTPrinter::addDeclParams(const DeclRef& declRef, List>* outParamRange) { 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(m_astBuilder, funcDeclRef)) { auto rangeStart = sb.getLength(); ParamDecl* paramDecl = paramDeclRef.getDecl(); auto paramType = getType(m_astBuilder, paramDeclRef); auto addParamElement = [&](Type* type, Index elementIndex) { if (!first) { sb << ", "; rangeStart += 2; } // Type part. { 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(type); } // 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; if (elementIndex != -1) sb << "_" << elementIndex; } } auto rangeEnd = sb.getLength(); if (outParamRange) outParamRange->add(makeRange(rangeStart, rangeEnd)); first = false; }; if (auto typePack = as(paramType)) { for (Index elementIndex = 0; elementIndex < typePack->getTypeCount(); ++elementIndex) { addParamElement(typePack->getElementType(elementIndex), elementIndex); } } else { addParamElement(paramType, -1); } } sb << ")"; } else if (auto genericDeclRef = declRef.as()) { addGenericParams(genericDeclRef); addDeclParams( m_astBuilder->getMemberDeclRef(genericDeclRef, genericDeclRef.getDecl()->inner), outParamRange); } else { } } void ASTPrinter::addDeclKindPrefix(Decl* decl) { if (auto genericDecl = as(decl)) { decl = genericDecl->inner; } for (auto modifier : decl->modifiers) { if (modifier->getKeywordName()) { if (m_optionFlags & OptionFlag::NoInternalKeywords) { if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; if (as(modifier)) continue; } // Don't print out attributes. if (as(modifier)) continue; m_builder << modifier->getKeywordName()->text << " "; } } if (as(decl)) { m_builder << "func "; } else if (as(decl)) { m_builder << "struct "; } else if (as(decl)) { m_builder << "interface "; } else if (as(decl)) { m_builder << "class "; } else if (auto typedefDecl = as(decl)) { m_builder << "typedef "; if (typedefDecl->type.type) { addType(typedefDecl->type.type); m_builder << " "; } } else if (const auto propertyDecl = as(decl)) { m_builder << "property "; } else if (as(decl)) { m_builder << "namespace "; } else if (auto varDecl = as(decl)) { if (varDecl->getType()) { addType(varDecl->getType()); m_builder << " "; } } else if (as(decl)) { m_builder << "enum "; } else if (auto enumCase = as(decl)) { if (enumCase->getType()) { addType(enumCase->getType()); m_builder << " "; } } else if (const auto assocType = as(decl)) { m_builder << "associatedtype "; } else if (const auto attribute = as(decl)) { m_builder << "attribute "; } } void ASTPrinter::addDeclResultType(const DeclRef& inDeclRef) { DeclRef declRef = inDeclRef; if (auto genericDeclRef = declRef.as()) { declRef = m_astBuilder->getMemberDeclRef(genericDeclRef, genericDeclRef.getDecl()->inner); } if (declRef.as()) { } else if (auto callableDeclRef = declRef.as()) { m_builder << " -> "; { ScopePart scopePart(this, Part::Type::ReturnType); addType(getResultType(m_astBuilder, callableDeclRef)); } } else if (auto propertyDecl = declRef.as()) { if (propertyDecl.getDecl()->type.type) { m_builder << " : "; addType(declRef.substitute(m_astBuilder, propertyDecl.getDecl()->type.type)); } } } /* 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::OptionFlag::NoInternalKeywords | ASTPrinter::OptionFlag::SimplifiedBuiltinType); 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