// 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::addExpr(Expr* expr) { if (!expr) { m_builder << ""; return; } auto& sb = m_builder; if (const auto incompleteExpr = as(expr)) { sb << ""; } else if (const auto varExpr = as(expr)) { if (varExpr->declRef) { addDeclPath(varExpr->declRef); } else if (varExpr->name) { sb << varExpr->name->text; } else { sb << ""; } } else if (const auto memberExpr = as(expr)) { if (memberExpr->baseExpression) { addExpr(memberExpr->baseExpression); sb << "."; } if (memberExpr->declRef) { _addDeclName(memberExpr->declRef.getDecl()); } else if (memberExpr->name) { sb << memberExpr->name->text; } else { sb << ""; } } else if (const auto staticMemberExpr = as(expr)) { if (staticMemberExpr->baseExpression) { addExpr(staticMemberExpr->baseExpression); sb << "::"; } if (staticMemberExpr->declRef) { _addDeclName(staticMemberExpr->declRef.getDecl()); } else if (staticMemberExpr->name) { sb << staticMemberExpr->name->text; } else { sb << ""; } } else if (const auto derefMemberExpr = as(expr)) { if (derefMemberExpr->baseExpression) { addExpr(derefMemberExpr->baseExpression); sb << "->"; } if (derefMemberExpr->declRef) { _addDeclName(derefMemberExpr->declRef.getDecl()); } else if (derefMemberExpr->name) { sb << derefMemberExpr->name->text; } else { sb << ""; } } else if (const auto intLit = as(expr)) { sb << intLit->value; // Handle suffix types without using getBaseTypeName switch (intLit->suffixType) { case BaseType::Int: // No suffix for default int break; case BaseType::UInt: sb << "u"; break; case BaseType::Int64: sb << "l"; break; case BaseType::UInt64: sb << "ul"; break; case BaseType::Int16: sb << "s"; break; case BaseType::UInt16: sb << "us"; break; case BaseType::Int8: sb << "b"; break; case BaseType::UInt8: sb << "ub"; break; default: // Don't add a suffix for other types break; } } else if (const auto floatLit = as(expr)) { sb << floatLit->value; // Handle suffix types without using getBaseTypeName switch (floatLit->suffixType) { case BaseType::Float: sb << "f"; break; case BaseType::Double: // No suffix for default double break; case BaseType::Half: sb << "h"; break; default: // Don't add a suffix for other types break; } } else if (const auto boolLit = as(expr)) { sb << (boolLit->value ? "true" : "false"); } else if (as(expr)) { sb << "nullptr"; } else if (as(expr)) { sb << "none"; } else if (const auto stringLit = as(expr)) { sb << "\"" << stringLit->value << "\""; } else if (const auto initList = as(expr)) { sb << "{"; bool first = true; for (auto arg : initList->args) { if (!first) sb << ", "; addExpr(arg); first = false; } sb << "}"; } else if (const auto arrayLengthExpr = as(expr)) { if (arrayLengthExpr->arrayExpr) { addExpr(arrayLengthExpr->arrayExpr); sb << ".Length"; } else { sb << ""; } } else if (const auto expandExpr = as(expr)) { sb << "..."; if (expandExpr->baseExpr) { addExpr(expandExpr->baseExpr); } } else if (const auto eachExpr = as(expr)) { sb << "each "; if (eachExpr->baseExpr) { addExpr(eachExpr->baseExpr); } } else if (const auto aggTypeCtorExpr = as(expr)) { addType(aggTypeCtorExpr->base.type); sb << "("; bool first = true; for (auto arg : aggTypeCtorExpr->arguments) { if (!first) sb << ", "; addExpr(arg); first = false; } sb << ")"; } else if (const auto invokeExpr = as(expr)) { if (const auto operatorExpr = as(invokeExpr)) { if (const auto infixExpr = as(operatorExpr)) { // Binary operator if (invokeExpr->arguments.getCount() == 2) { sb << "("; addExpr(invokeExpr->arguments[0]); if (operatorExpr->functionExpr && as(operatorExpr->functionExpr) && as(operatorExpr->functionExpr)->name) { sb << " " << as(operatorExpr->functionExpr)->name->text << " "; } else { sb << " "; } addExpr(invokeExpr->arguments[1]); sb << ")"; return; } } else if (const auto prefixExpr = as(operatorExpr)) { // Prefix operator if (operatorExpr->functionExpr && as(operatorExpr->functionExpr) && as(operatorExpr->functionExpr)->name) { sb << as(operatorExpr->functionExpr)->name->text; } else { sb << ""; } if (invokeExpr->arguments.getCount() > 0) { addExpr(invokeExpr->arguments[0]); } return; } else if (const auto postfixExpr = as(operatorExpr)) { // Postfix operator if (invokeExpr->arguments.getCount() > 0) { addExpr(invokeExpr->arguments[0]); } if (operatorExpr->functionExpr && as(operatorExpr->functionExpr) && as(operatorExpr->functionExpr)->name) { sb << as(operatorExpr->functionExpr)->name->text; } else { sb << ""; } return; } else if (const auto selectExpr = as(operatorExpr)) { // Ternary operator: cond ? ifTrue : ifFalse if (invokeExpr->arguments.getCount() == 3) { addExpr(invokeExpr->arguments[0]); sb << " ? "; addExpr(invokeExpr->arguments[1]); sb << " : "; addExpr(invokeExpr->arguments[2]); return; } } else if (const auto logicExpr = as(operatorExpr)) { // Logical operators with short-circuit behavior if (invokeExpr->arguments.getCount() == 2) { addExpr(invokeExpr->arguments[0]); sb << (logicExpr->flavor == LogicOperatorShortCircuitExpr::And ? " && " : " || "); addExpr(invokeExpr->arguments[1]); return; } } } // Regular function call if (invokeExpr->functionExpr) { addExpr(invokeExpr->functionExpr); } else { sb << ""; } sb << "("; bool first = true; for (auto arg : invokeExpr->arguments) { if (!first) sb << ", "; addExpr(arg); first = false; } sb << ")"; } else if (const auto indexExpr = as(expr)) { if (indexExpr->baseExpression) { addExpr(indexExpr->baseExpression); } sb << "["; bool first = true; for (auto i : indexExpr->indexExprs) { if (!first) sb << ", "; addExpr(i); first = false; } sb << "]"; } else if (const auto swizzleExpr = as(expr)) { if (swizzleExpr->base) { addExpr(swizzleExpr->base); } sb << "."; // Print swizzle components (like .xyzw or .rgba) static const char* xyzwComponents = "xyzw"; // Choose component naming based on type if possible const char* components = xyzwComponents; for (auto index : swizzleExpr->elementIndices) { if (index < 4) { sb << components[index]; } else { sb << "?"; } } } else if (const auto matrixSwizzleExpr = as(expr)) { if (matrixSwizzleExpr->base) { addExpr(matrixSwizzleExpr->base); } sb << "."; // Print matrix swizzle components (like _m00, _m01, etc.) for (int i = 0; i < matrixSwizzleExpr->elementCount; ++i) { if (i > 0) sb << ""; sb << "_m" << matrixSwizzleExpr->elementCoords[i].row << matrixSwizzleExpr->elementCoords[i].col; } } else if (const auto makeRefExpr = as(expr)) { sb << "&"; if (makeRefExpr->base) { addExpr(makeRefExpr->base); } } else if (const auto derefExpr = as(expr)) { sb << "*"; if (derefExpr->base) { addExpr(derefExpr->base); } } else if (const auto typeCastExpr = as(expr)) { if (as(typeCastExpr)) { sb << "("; addType(expr->type); sb << ")"; if (typeCastExpr->arguments.getCount() > 0) { addExpr(typeCastExpr->arguments[0]); } } else if ( as(typeCastExpr) || as(typeCastExpr) || as(typeCastExpr) || as(typeCastExpr)) { // For implicit casts, just print the inner expression if (typeCastExpr->arguments.getCount() > 0) { addExpr(typeCastExpr->arguments[0]); } else { sb << ""; } } } else if (const auto builtinCastExpr = as(expr)) { if (builtinCastExpr->base) { addExpr(builtinCastExpr->base); } else { sb << ""; } } else if (const auto castToSuperTypeExpr = as(expr)) { sb << "(("; addType(expr->type); sb << ")"; if (castToSuperTypeExpr->valueArg) { addExpr(castToSuperTypeExpr->valueArg); } sb << ")"; } else if (const auto isTypeExpr = as(expr)) { if (isTypeExpr->value) { addExpr(isTypeExpr->value); } sb << " is "; if (isTypeExpr->typeExpr.type) { addType(isTypeExpr->typeExpr.type); } else { sb << ""; } } else if (const auto asTypeExpr = as(expr)) { if (asTypeExpr->value) { addExpr(asTypeExpr->value); } sb << " as "; if (asTypeExpr->typeExpr) { addExpr(asTypeExpr->typeExpr); } else { sb << ""; } } else if (const auto sizeOfExpr = as(expr)) { sb << "sizeof("; if (sizeOfExpr->sizedType) { addType(sizeOfExpr->sizedType); } else if (sizeOfExpr->value) { addExpr(sizeOfExpr->value); } sb << ")"; } else if (const auto alignOfExpr = as(expr)) { sb << "alignof("; if (alignOfExpr->sizedType) { addType(alignOfExpr->sizedType); } else if (alignOfExpr->value) { addExpr(alignOfExpr->value); } sb << ")"; } else if (const auto countOfExpr = as(expr)) { sb << "countof("; if (countOfExpr->sizedType) { addType(countOfExpr->sizedType); } else if (countOfExpr->value) { addExpr(countOfExpr->value); } sb << ")"; } else if (const auto addressOfExpr = as(expr)) { sb << "__getAddress("; if (addressOfExpr->arg) { addExpr(addressOfExpr->arg); } sb << ")"; } else if (const auto makeOptionalExpr = as(expr)) { if (makeOptionalExpr->value) { sb << "Optional("; addExpr(makeOptionalExpr->value); sb << ")"; } else { sb << "Optional<"; if (makeOptionalExpr->typeExpr) { addExpr(makeOptionalExpr->typeExpr); } else { addType(expr->type); } sb << ">.none"; } } else if (const auto modifierCastExpr = as(expr)) { sb << "("; addType(expr->type); sb << ")"; if (modifierCastExpr->valueArg) { addExpr(modifierCastExpr->valueArg); } } else if (const auto assignExpr = as(expr)) { if (assignExpr->left) { addExpr(assignExpr->left); } sb << " = "; if (assignExpr->right) { addExpr(assignExpr->right); } } else if (const auto parenExpr = as(expr)) { sb << "("; if (parenExpr->base) { addExpr(parenExpr->base); } sb << ")"; } else if (as(expr)) { sb << "this"; } else if (as(expr)) { sb << "__return_val"; } else if (const auto letExpr = as(expr)) { sb << "let "; if (letExpr->decl) { _addDeclName(letExpr->decl); if (letExpr->decl->type.type) { sb << " : "; addType(letExpr->decl->type.type); } if (letExpr->decl->initExpr) { sb << " = "; addExpr(letExpr->decl->initExpr); } } if (letExpr->body) { sb << " in "; addExpr(letExpr->body); } } else if (const auto extractExistentialValueExpr = as(expr)) { if (extractExistentialValueExpr->declRef) { addDeclPath(extractExistentialValueExpr->declRef); } else if (extractExistentialValueExpr->originalExpr) { addExpr(extractExistentialValueExpr->originalExpr); } else { sb << ""; } } else if (const auto openRefExpr = as(expr)) { sb << "open("; if (openRefExpr->innerExpr) { addExpr(openRefExpr->innerExpr); } sb << ")"; } else if (const auto detachExpr = as(expr)) { sb << "detach("; if (detachExpr->inner) { addExpr(detachExpr->inner); } sb << ")"; } else if (const auto higherOrderInvokeExpr = as(expr)) { if (const auto primalSubstituteExpr = as(higherOrderInvokeExpr)) { sb << "__primal("; } else if (const auto forwardDiffExpr = as(higherOrderInvokeExpr)) { sb << "__fwd_diff("; } else if (const auto backwardDiffExpr = as(higherOrderInvokeExpr)) { sb << "__bwd_diff("; } else if (const auto dispatchKernelExpr = as(higherOrderInvokeExpr)) { sb << "__dispatch_kernel("; } else { sb << "("; } if (higherOrderInvokeExpr->baseFunction) { addExpr(higherOrderInvokeExpr->baseFunction); } // Add additional parameters for specific higher-order expressions if (const auto dispatchKernelExpr = as(higherOrderInvokeExpr)) { sb << ", "; if (dispatchKernelExpr->threadGroupSize) { addExpr(dispatchKernelExpr->threadGroupSize); } else { sb << ""; } sb << ", "; if (dispatchKernelExpr->dispatchSize) { addExpr(dispatchKernelExpr->dispatchSize); } else { sb << ""; } } sb << ")"; } else if (const auto treatAsDiffExpr = as(expr)) { if (treatAsDiffExpr->flavor == TreatAsDifferentiableExpr::NoDiff) { sb << "no_diff("; } else { sb << "differentiable("; } if (treatAsDiffExpr->innerExpr) { addExpr(treatAsDiffExpr->innerExpr); } sb << ")"; } else if (as(expr)) { sb << "This"; } else if (const auto andTypeExpr = as(expr)) { if (andTypeExpr->left.type) { addType(andTypeExpr->left.type); } else { sb << ""; } sb << " & "; if (andTypeExpr->right.type) { addType(andTypeExpr->right.type); } else { sb << ""; } } else if (const auto modifiedTypeExpr = as(expr)) { // Print modifiers for (auto modifier : modifiedTypeExpr->modifiers) { if (modifier->getKeywordName()) { sb << modifier->getKeywordName()->text << " "; } } if (modifiedTypeExpr->base.type) { addType(modifiedTypeExpr->base.type); } else { sb << ""; } } else if (const auto pointerTypeExpr = as(expr)) { if (pointerTypeExpr->base.type) { addType(pointerTypeExpr->base.type); } else { sb << ""; } sb << "*"; } else if (const auto funcTypeExpr = as(expr)) { sb << "("; bool first = true; for (auto& param : funcTypeExpr->parameters) { if (!first) sb << ", "; if (param.type) { addType(param.type); } else { sb << ""; } first = false; } sb << ") -> "; if (funcTypeExpr->result.type) { addType(funcTypeExpr->result.type); } else { sb << ""; } } else if (const auto tupleTypeExpr = as(expr)) { sb << "("; bool first = true; for (auto& member : tupleTypeExpr->members) { if (!first) sb << ", "; if (member.type) { addType(member.type); } else { sb << ""; } first = false; } sb << ")"; } else if (const auto partiallyAppliedGenericExpr = as(expr)) { if (partiallyAppliedGenericExpr->baseGenericDeclRef) { addDeclPath(partiallyAppliedGenericExpr->baseGenericDeclRef); } else if (partiallyAppliedGenericExpr->originalExpr) { addExpr(partiallyAppliedGenericExpr->originalExpr); } else { sb << ""; } sb << "<"; bool first = true; for (auto arg : partiallyAppliedGenericExpr->knownGenericArgs) { if (!first) sb << ", "; addVal(arg); first = false; } sb << ", ...>"; } else if (const auto packExpr = as(expr)) { sb << "("; bool first = true; for (auto arg : packExpr->args) { if (!first) sb << ", "; addExpr(arg); first = false; } sb << ")"; } else if (const auto spirvAsmExpr = as(expr)) { sb << "spirv_asm {"; bool first = true; for (auto& inst : spirvAsmExpr->insts) { if (!first) sb << "\n "; else sb << " "; // Print opcode if (inst.opcode.flavor == SPIRVAsmOperand::NamedValue && inst.opcode.token.type != TokenType::Unknown) { sb << inst.opcode.token.getContent(); } else { sb << ""; } // Print operands for (auto& operand : inst.operands) { sb << " "; switch (operand.flavor) { case SPIRVAsmOperand::Literal: if (operand.token.type != TokenType::Unknown) sb << operand.token.getContent(); else sb << operand.knownValue; break; case SPIRVAsmOperand::Id: sb << "%" << operand.token.getContent(); break; case SPIRVAsmOperand::ResultMarker: sb << "result"; break; case SPIRVAsmOperand::NamedValue: sb << operand.token.getContent(); break; case SPIRVAsmOperand::SlangValue: case SPIRVAsmOperand::SlangValueAddr: case SPIRVAsmOperand::SlangImmediateValue: if (operand.expr) addExpr(operand.expr); else sb << ""; break; case SPIRVAsmOperand::SlangType: case SPIRVAsmOperand::SampledType: case SPIRVAsmOperand::ImageType: case SPIRVAsmOperand::SampledImageType: if (operand.type.type) addType(operand.type.type); else sb << ""; break; default: sb << ""; break; } } first = false; } sb << " }"; } else if (const auto genericAppExpr = as(expr)) { if (genericAppExpr->functionExpr) { addExpr(genericAppExpr->functionExpr); } else { sb << ""; } sb << "<"; bool first = true; for (auto arg : genericAppExpr->arguments) { if (!first) sb << ", "; addExpr(arg); first = false; } sb << ">"; } else if (const auto tryExpr = as(expr)) { switch (tryExpr->tryClauseType) { case TryClauseType::Standard: sb << "try "; break; case TryClauseType::Optional: sb << "try? "; break; case TryClauseType::Assert: sb << "try! "; break; default: break; } if (tryExpr->base) { addExpr(tryExpr->base); } } else if (const auto defaultConstructExpr = as(expr)) { sb << "default("; addType(expr->type); sb << ")"; } else if (const auto overloadedExpr = as(expr)) { if (overloadedExpr->base) { addExpr(overloadedExpr->base); sb << "."; } if (overloadedExpr->name) { sb << overloadedExpr->name->text; } else { sb << ""; } } else if (const auto overloadedExpr2 = as(expr)) { if (overloadedExpr2->base) { addExpr(overloadedExpr2->base); sb << "."; } sb << ""; } else { // For any other expression types sb << ""; } } void ASTPrinter::addVal(Val* val) { val->toText(m_builder); } /* static */ void ASTPrinter::appendDeclName(Decl* decl, StringBuilder& out) { decl = maybeGetInner(decl); 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. DeclRef parentDeclRef; // If this is a lookup decl ref, prefix with the lookup source type instead of the parent. if (auto lookupDeclRef = as(declRef.declRefBase)) { if (auto extractExistentialType = as(lookupDeclRef->getLookupSource())) { parentDeclRef = extractExistentialType->getOriginalInterfaceDeclRef(); } else { parentDeclRef = isDeclRefTypeOf(lookupDeclRef->getLookupSource()); } if (as(parentDeclRef.getDecl())) { if (auto baseLookupDeclRef = as(parentDeclRef.declRefBase)) { // If the base type is a lookup, we want to use its source type parentDeclRef = isDeclRefTypeOf(baseLookupDeclRef->getLookupSource()); } } } else { 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 (parentDeclRef.as()) { sb << "This."; } else if (parentDeclRef.as() || parentDeclRef.as()) { _addDeclPathRec(parentDeclRef, 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()) { Type* type = getTargetType(m_astBuilder, extensionDeclRef); 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, nullptr); } } } struct ParamScope { StringBuilder* sb; List>* paramRanges; Index rangeStart; ParamScope(StringBuilder* inSb, List>* outParamRanges) : sb(inSb), paramRanges(outParamRanges) { rangeStart = sb->getLength(); } ~ParamScope() { if (paramRanges) { Index rangeEnd = sb->getLength(); paramRanges->add(makeRange(rangeStart, rangeEnd)); } } }; void ASTPrinter::addGenericParams( const DeclRef& genericDeclRef, List>* outParamRanges) { 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; ParamScope paramScope(&sb, outParamRanges); { ScopePart scopePart(this, Part::Type::GenericParamType); sb << getText(genericTypeParam.getName()); } } else if (auto genericValParam = paramDeclRef.as()) { if (!first) sb << ", "; first = false; ParamScope paramScope(&sb, outParamRanges); { ScopePart scopePart(this, Part::Type::GenericParamValueType); addType(getType(m_astBuilder, genericValParam)); } sb << " "; { ScopePart scopePart(this, Part::Type::GenericParamValue); sb << getText(genericValParam.getName()); } } else if (auto genericTypePackParam = paramDeclRef.as()) { if (!first) sb << ", "; first = false; ParamScope paramScope(&sb, outParamRanges); { 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)) { ParamDecl* paramDecl = paramDeclRef.getDecl(); auto paramType = getType(m_astBuilder, paramDeclRef); auto addParamElement = [&](Type* type, Index elementIndex) { if (!first) { sb << ", "; } ParamScope paramScope(&sb, outParamRange); // 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; } } if (m_optionFlags & OptionFlag::DefaultParamValues && paramDecl->initExpr) { sb << " = "; addExpr(paramDecl->initExpr); } 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, 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; 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