diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2021-03-01 15:37:46 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-01 15:37:46 -0500 |
| commit | 837a155b3d33035ee0739858f4ab25c65048ad6c (patch) | |
| tree | 7abc4859a6669c9b8e0bcdaad6eb7b37471679ba | |
| parent | b3501add6c4dda798acf7b84b71b638d8d0b7898 (diff) | |
Doc improvements (#1729)
* #include an absolute path didn't work - because paths were taken to always be relative.
* Split out AST 'printing'.
* Replace listener with List<Section>
* Section -> Part.
* Kind -> Type Flags -> Kind for ASTPrinter::Part
* Improve comments around ASTPrinter.
* toString -> toText on Val derived types. toText appends to a StringBuilder.
* Added toSlice free function.
Added operator<< for Val derived types.
Use << where appropriate in doing toText.
* More work at mark down output.
* Fill in sourceloc for enum case.
Add more sophisticated location determination for EnumCase.
Refactored documentation output into DocMarkdownWriter.
* Improvements for sig output.
| -rw-r--r-- | build/visual-studio/slang/slang.vcxproj | 2 | ||||
| -rw-r--r-- | build/visual-studio/slang/slang.vcxproj.filters | 6 | ||||
| -rw-r--r-- | source/core/slang-string.h | 4 | ||||
| -rw-r--r-- | source/slang/slang-ast-base.h | 8 | ||||
| -rw-r--r-- | source/slang/slang-ast-print.cpp | 274 | ||||
| -rw-r--r-- | source/slang/slang-ast-print.h | 162 | ||||
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ast-type.cpp | 133 | ||||
| -rw-r--r-- | source/slang/slang-ast-type.h | 34 | ||||
| -rw-r--r-- | source/slang/slang-ast-val.cpp | 78 | ||||
| -rw-r--r-- | source/slang/slang-ast-val.h | 16 | ||||
| -rw-r--r-- | source/slang/slang-check-expr.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 16 | ||||
| -rw-r--r-- | source/slang/slang-check-overload.cpp | 198 | ||||
| -rw-r--r-- | source/slang/slang-doc.cpp | 389 | ||||
| -rw-r--r-- | source/slang/slang-doc.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-parser.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-syntax.cpp | 29 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 50 |
19 files changed, 975 insertions, 438 deletions
diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj index 7ddc3a2e0..e2d91ea76 100644 --- a/build/visual-studio/slang/slang.vcxproj +++ b/build/visual-studio/slang/slang.vcxproj @@ -194,6 +194,7 @@ <ClInclude Include="..\..\..\source\slang\slang-ast-dump.h" /> <ClInclude Include="..\..\..\source\slang\slang-ast-expr.h" /> <ClInclude Include="..\..\..\source\slang\slang-ast-modifier.h" /> + <ClInclude Include="..\..\..\source\slang\slang-ast-print.h" /> <ClInclude Include="..\..\..\source\slang\slang-ast-reflect.h" /> <ClInclude Include="..\..\..\source\slang\slang-ast-stmt.h" /> <ClInclude Include="..\..\..\source\slang\slang-ast-support-types.h" /> @@ -317,6 +318,7 @@ <ClCompile Include="..\..\..\source\slang\slang-ast-builder.cpp" /> <ClCompile Include="..\..\..\source\slang\slang-ast-decl.cpp" /> <ClCompile Include="..\..\..\source\slang\slang-ast-dump.cpp" /> + <ClCompile Include="..\..\..\source\slang\slang-ast-print.cpp" /> <ClCompile Include="..\..\..\source\slang\slang-ast-reflect.cpp" /> <ClCompile Include="..\..\..\source\slang\slang-ast-substitutions.cpp" /> <ClCompile Include="..\..\..\source\slang\slang-ast-type.cpp" /> diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters index f8bc02d51..e27409c5d 100644 --- a/build/visual-studio/slang/slang.vcxproj.filters +++ b/build/visual-studio/slang/slang.vcxproj.filters @@ -33,6 +33,9 @@ <ClInclude Include="..\..\..\source\slang\slang-ast-modifier.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\..\..\source\slang\slang-ast-print.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="..\..\..\source\slang\slang-ast-reflect.h"> <Filter>Header Files</Filter> </ClInclude> @@ -398,6 +401,9 @@ <ClCompile Include="..\..\..\source\slang\slang-ast-dump.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\..\source\slang\slang-ast-print.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\..\..\source\slang\slang-ast-reflect.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/source/core/slang-string.h b/source/core/slang-string.h index 3fb184ac7..2312c0439 100644 --- a/source/core/slang-string.h +++ b/source/core/slang-string.h @@ -181,6 +181,10 @@ namespace Slang char const* m_end; }; + // A more convenient way to make slices from *string literals* + template <size_t SIZE> + SLANG_FORCE_INLINE UnownedStringSlice toSlice(const char (&in)[SIZE]) { return UnownedStringSlice(in, SIZE - 1); } + // A `StringRepresentation` provides the backing storage for // all reference-counted string-related types. class StringRepresentation : public RefObject diff --git a/source/slang/slang-ast-base.h b/source/slang/slang-ast-base.h index b7810b49a..bb92c4738 100644 --- a/source/slang/slang-ast-base.h +++ b/source/slang/slang-ast-base.h @@ -98,7 +98,11 @@ class Val : public NodeBase Val* substituteImpl(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); bool equalsVal(Val* val); + + // Appends as text to the end of the builder + void toText(StringBuilder& out); String toString(); + HashCode getHashCode(); bool operator == (const Val & v) { @@ -108,10 +112,12 @@ class Val : public NodeBase // Overrides should be public so base classes can access Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); bool _equalsValOverride(Val* val); - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); HashCode _getHashCodeOverride(); }; +SLANG_FORCE_INLINE StringBuilder& operator<<(StringBuilder& io, Val* val) { SLANG_ASSERT(val); val->toText(io); return io; } + class Type; template <typename T> diff --git a/source/slang/slang-ast-print.cpp b/source/slang/slang-ast-print.cpp new file mode 100644 index 000000000..29c034659 --- /dev/null +++ b/source/slang/slang-ast-print.cpp @@ -0,0 +1,274 @@ +// 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); +} + +void ASTPrinter::_addDeclName(Decl* decl) +{ + if (as<ConstructorDecl>(decl)) + { + m_builder << "init"; + } + else if (as<SubscriptDecl>(decl)) + { + m_builder << "subscript"; + } + else + { + m_builder << getText(decl->getName()); + } +} + +void ASTPrinter::addDeclPath(const DeclRef<Decl>& declRef) +{ + ScopePart scopePart(this, Part::Type::DeclPath); + _addDeclPathRec(declRef); +} + +void ASTPrinter::_addDeclPathRec(const DeclRef<Decl>& declRef) +{ + 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<GenericDecl>(); + if (parentGenericDeclRef) + { + parentDeclRef = parentGenericDeclRef.getParent(); + } + + // Depending on what the parent is, we may want to format things specially + if (auto aggTypeDeclRef = parentDeclRef.as<AggTypeDecl>()) + { + _addDeclPathRec(aggTypeDeclRef); + sb << "."; + } + + _addDeclName(declRef.getDecl()); + + // If the parent declaration is a generic, then we need to print out its + // signature + if (parentGenericDeclRef) + { + auto genSubst = as<GenericSubstitution>(declRef.substitutions.substitutions); + 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<Witness>(arg)) + continue; + + if (!first) sb << ", "; + addVal(arg); + first = false; + } + sb << ">"; + } +} + +void ASTPrinter::addDeclParams(const DeclRef<Decl>& declRef) +{ + auto& sb = m_builder; + + if (auto funcDeclRef = declRef.as<CallableDecl>()) + { + // 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); + 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<GenericDecl>()) + { + sb << "<"; + bool first = true; + for (auto paramDeclRef : getMembers(genericDeclRef)) + { + if (auto genericTypeParam = paramDeclRef.as<GenericTypeParamDecl>()) + { + if (!first) sb << ", "; + first = false; + + { + ScopePart scopePart(this, Part::Type::GenericParamType); + sb << getText(genericTypeParam.getName()); + } + } + else if (auto genericValParam = paramDeclRef.as<GenericValueParamDecl>()) + { + 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 << ">"; + + addDeclParams(DeclRef<Decl>(getInner(genericDeclRef), genericDeclRef.substitutions)); + } + else + { + } +} + +void ASTPrinter::addDeclKindPrefix(Decl* decl) +{ + if (auto genericDecl = as<GenericDecl>(decl)) + { + decl = genericDecl->inner; + } + if (as<FuncDecl>(decl)) + { + m_builder << "func "; + } +} + +void ASTPrinter::addDeclResultType(const DeclRef<Decl>& inDeclRef) +{ + DeclRef<Decl> declRef = inDeclRef; + if (auto genericDeclRef = declRef.as<GenericDecl>()) + { + declRef = DeclRef<Decl>(getInner(genericDeclRef), genericDeclRef.substitutions); + } + + if (as<ConstructorDecl>(declRef)) + { + } + else if (auto callableDeclRef = declRef.as<CallableDecl>()) + { + m_builder << " -> "; + + { + ScopePart scopePart(this, Part::Type::ReturnType); + addType(getResultType(m_astBuilder, callableDeclRef)); + } + } +} + +/* static */void ASTPrinter::addDeclSignature(const DeclRef<Decl>& declRef) +{ + addDeclKindPrefix(declRef.getDecl()); + addDeclPath(declRef); + addDeclParams(declRef); + addDeclResultType(declRef); +} + +/* static */String ASTPrinter::getDeclSignatureString(DeclRef<Decl> 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<Part>& parts) +{ + const Index index = parts.findFirstIndex([&](const Part& part) -> bool { return part.type == partType; }); + return index >= 0 ? getPart(slice, parts[index]) : UnownedStringSlice(); +} + +UnownedStringSlice ASTPrinter::getPart(Part::Type partType) const +{ + return m_parts ? getPart(partType, getSlice(), *m_parts) : UnownedStringSlice(); +} + + +} // namespace Slang diff --git a/source/slang/slang-ast-print.h b/source/slang/slang-ast-print.h new file mode 100644 index 000000000..7fb818898 --- /dev/null +++ b/source/slang/slang-ast-print.h @@ -0,0 +1,162 @@ +// slang-ast-print.h + +#ifndef SLANG_AST_PRINT_H +#define SLANG_AST_PRINT_H + +#include "slang-ast-all.h" + +namespace Slang { + +class ASTPrinter +{ +public: + typedef uint32_t OptionFlags; + struct OptionFlag + { + enum Enum : OptionFlags + { + ParamNames = 0x1, ///< If set will output parameter names + }; + }; + + /// Note that we could/can have a hierarchy of Parts - with overlapping spans. + /// Moreover we could have less kinds, if we used the overlaps to signal out sections + /// + /// For example we could have a 'Param', 'Generic' span, and then have 'Name', 'Type' and 'Value'. + /// So a param type, would be the 'Type' defined in a Param span. Moreover you could have the hierarchy of Types, and then + /// such that you can pull out specific parts that make up a type. + /// + /// This is powerful/flexible - but requires more complexity at the use sites, so for now we use this simpler mechanism. + + /// Defines part of the structure of the output printed. + struct Part + { + enum class Kind + { + None, + Type, + Value, + Name, + }; + + enum class Type + { + None, + ParamType, ///< The type associated with a parameter + ParamName, ///< The name associated with a parameter + ReturnType, ///< The return type + DeclPath, ///< The declaration path (NOT including the actual decl name) + GenericParamType, ///< Generic parameter type + GenericParamValue, ///< Generic parameter value + GenericParamValueType, ///< The type requirement for a value type + }; + + static Kind getKind(Type type); + static Part make(Type type, Index start, Index end) { Part part; part.type = type; part.start = start; part.end = end; return part; } + + Type type = Type::None; + Index start; + Index end; + }; + + struct PartPair + { + Part first; + Part second; + }; + + struct ScopePart + { + ScopePart(ASTPrinter* printer, Part::Type type): + m_printer(printer), + m_type(type), + m_startIndex(printer->m_builder.getLength()) + { + } + ~ScopePart() + { + List<Part>* parts = m_printer->m_parts; + if (parts) + { + parts->add(Part::make(m_type, m_startIndex, m_printer->m_builder.getLength())); + } + } + + Part::Type m_type; + Index m_startIndex; + ASTPrinter* m_printer; + }; + + /// We might want options to change how things are output, for example we may want to output parameter names + /// if there are any + + /// Get the currently built up string + StringBuilder& getStringBuilder() { return m_builder; } + /// Get the current offset, for the end of the string builder - useful for building up ranges + Index getOffset() const { return m_builder.getLength(); } + + /// Reset the state + void reset() { m_builder.Clear(); } + + /// Get the current string + String getString() { return m_builder.ProduceString(); } + + /// Get contents as a slice + UnownedStringSlice getSlice() const { return m_builder.getUnownedSlice(); } + + /// Add a type + void addType(Type* type); + /// Add a value + void addVal(Val* val); + + /// Add the path to the declaration including the declaration name + void addDeclPath(const DeclRef<Decl>& declRef); + + /// Add just the parameters from a declaration. + /// Will output the generic parameters (if it's a generic) in <> before the parameters () + void addDeclParams(const DeclRef<Decl>& declRef); + + /// Add a prefix that describes the kind of declaration + void addDeclKindPrefix(Decl* decl); + + /// Add the result type + /// Should be called after the decl params + void addDeclResultType(const DeclRef<Decl>& inDeclRef); + + /// Add the signature for the decl + void addDeclSignature(const DeclRef<Decl>& declRef); + + /// Get the specified part type. Returns empty slice if not found + UnownedStringSlice getPart(Part::Type partType) const; + /// Get the slice for a part + UnownedStringSlice getPartSlice(const Part& part) const { return getPart(getSlice(), part); } + + /// Gets the specified part type + static UnownedStringSlice getPart(const UnownedStringSlice& slice, const Part& part) { return UnownedStringSlice(slice.begin() + part.start, slice.begin() + part.end); } + static UnownedStringSlice getPart(Part::Type partType, const UnownedStringSlice& slice, const List<Part>& parts); + + /// Ctor + ASTPrinter(ASTBuilder* astBuilder, OptionFlags optionFlags = 0, List<Part>* parts = nullptr): + m_astBuilder(astBuilder), + m_parts(parts), + m_optionFlags(optionFlags) + { + } + + static String getDeclSignatureString(const LookupResultItem& item, ASTBuilder* astBuilder); + static String getDeclSignatureString(DeclRef<Decl> declRef, ASTBuilder* astBuilder); + +protected: + + void _addDeclPathRec(const DeclRef<Decl>& declRef); + void _addDeclName(Decl* decl); + + OptionFlags m_optionFlags; ///< Flags controlling output + List<Part>* m_parts; ///< Optional parts list + ASTBuilder* m_astBuilder; ///< Required as types are setup as part of printing + StringBuilder m_builder; ///< The output of the 'printing' process +}; + +} // namespace Slang + +#endif // SLANG_AST_PRINT_H diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 8338f0b21..6e4c90cc4 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -652,6 +652,7 @@ namespace Slang // Debugging: String toString() const; + void toText(StringBuilder& out) const; }; template<typename T> @@ -721,6 +722,8 @@ namespace Slang } }; + SLANG_FORCE_INLINE StringBuilder& operator<<(StringBuilder& io, const DeclRefBase& declRef) { declRef.toText(io); return io; } + template<typename T> DeclRef<T> DeclRefBase::as() const { diff --git a/source/slang/slang-ast-type.cpp b/source/slang/slang-ast-type.cpp index 79df8e48b..64b8c7515 100644 --- a/source/slang/slang-ast-type.cpp +++ b/source/slang/slang-ast-type.cpp @@ -77,9 +77,9 @@ Type* Type::getCanonicalType() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! OverloadGroupType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String OverloadGroupType::_toStringOverride() +void OverloadGroupType::_toTextOverride(StringBuilder& out) { - return "overload group"; + out << toSlice("overload group"); } bool OverloadGroupType::_equalsImplOverride(Type * /*type*/) @@ -99,9 +99,9 @@ HashCode OverloadGroupType::_getHashCodeOverride() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! InitializerListType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String InitializerListType::_toStringOverride() +void InitializerListType::_toTextOverride(StringBuilder& out) { - return "initializer list"; + out << toSlice("initializer list"); } bool InitializerListType::_equalsImplOverride(Type * /*type*/) @@ -121,9 +121,9 @@ HashCode InitializerListType::_getHashCodeOverride() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ErrorType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String ErrorType::_toStringOverride() +void ErrorType::_toTextOverride(StringBuilder& out) { - return "error"; + out << toSlice("error"); } bool ErrorType::_equalsImplOverride(Type* type) @@ -150,9 +150,9 @@ HashCode ErrorType::_getHashCodeOverride() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DeclRefType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String DeclRefType::_toStringOverride() +void DeclRefType::_toTextOverride(StringBuilder& out) { - return declRef.toString(); + out << declRef; } HashCode DeclRefType::_getHashCodeOverride() @@ -306,11 +306,9 @@ BasicExpressionType* BasicExpressionType::_getScalarTypeOverride() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VectorExpressionType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String VectorExpressionType::_toStringOverride() +void VectorExpressionType::_toTextOverride(StringBuilder& out) { - StringBuilder sb; - sb << "vector<" << elementType->toString() << "," << elementCount->toString() << ">"; - return sb.ProduceString(); + out << toSlice("vector<") << elementType << toSlice(",") << elementCount << toSlice(">"); } BasicExpressionType* VectorExpressionType::_getScalarTypeOverride() @@ -320,11 +318,9 @@ BasicExpressionType* VectorExpressionType::_getScalarTypeOverride() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! MatrixExpressionType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String MatrixExpressionType::_toStringOverride() +void MatrixExpressionType::_toTextOverride(StringBuilder& out) { - StringBuilder sb; - sb << "matrix<" << getElementType()->toString() << "," << getRowCount()->toString() << "," << getColumnCount()->toString() << ">"; - return sb.ProduceString(); + out << toSlice("matrix<") << getElementType() << toSlice(",") << getRowCount() << toSlice(",") << getColumnCount() << toSlice(">"); } BasicExpressionType* MatrixExpressionType::_getScalarTypeOverride() @@ -401,21 +397,22 @@ HashCode ArrayExpressionType::_getHashCodeOverride() return baseType->getHashCode(); } -Slang::String ArrayExpressionType::_toStringOverride() +void ArrayExpressionType::_toTextOverride(StringBuilder& out) { + out << baseType; + out.appendChar('['); if (arrayLength) - return baseType->toString() + "[" + arrayLength->toString() + "]"; - else - return baseType->toString() + "[]"; + { + out << arrayLength; + } + out.appendChar(']'); } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TypeType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String TypeType::_toStringOverride() +void TypeType::_toTextOverride(StringBuilder& out) { - StringBuilder sb; - sb << "typeof(" << type->toString() << ")"; - return sb.ProduceString(); + out << toSlice("typeof(") << type << toSlice(")"); } bool TypeType::_equalsImplOverride(Type * t) @@ -440,10 +437,10 @@ HashCode TypeType::_getHashCodeOverride() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! GenericDeclRefType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String GenericDeclRefType::_toStringOverride() +void GenericDeclRefType::_toTextOverride(StringBuilder& out) { // TODO: what is appropriate here? - return "<DeclRef<GenericDecl>>"; + out << toSlice("<DeclRef<GenericDecl>>"); } bool GenericDeclRefType::_equalsImplOverride(Type * type) @@ -467,12 +464,9 @@ Type* GenericDeclRefType::_createCanonicalTypeOverride() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NamespaceType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String NamespaceType::_toStringOverride() +void NamespaceType::_toTextOverride(StringBuilder& out) { - String result; - result.append("namespace "); - result.append(declRef.toString()); - return result; + out << toSlice("namespace ") << declRef; } bool NamespaceType::_equalsImplOverride(Type * type) @@ -504,9 +498,13 @@ Type* PtrTypeBase::getValueType() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NamedExpressionType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String NamedExpressionType::_toStringOverride() +void NamedExpressionType::_toTextOverride(StringBuilder& out) { - return getText(declRef.getName()); + Name* name = declRef.getName(); + if (name) + { + out << name->text; + } } bool NamedExpressionType::_equalsImplOverride(Type * /*type*/) @@ -538,19 +536,19 @@ HashCode NamedExpressionType::_getHashCodeOverride() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! FuncType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String FuncType::_toStringOverride() +void FuncType::_toTextOverride(StringBuilder& out) { - StringBuilder sb; - sb << "("; - UInt paramCount = getParamCount(); - for (UInt pp = 0; pp < paramCount; ++pp) + out << toSlice("("); + Index paramCount = getParamCount(); + for (Index pp = 0; pp < paramCount; ++pp) { - if (pp != 0) sb << ", "; - sb << getParamType(pp)->toString(); + if (pp != 0) + { + out << toSlice(", "); + } + out << getParamType(pp); } - sb << ") -> "; - sb << getResultType()->toString(); - return sb.ProduceString(); + out << toSlice(") -> ") << getResultType(); } bool FuncType::_equalsImplOverride(Type * type) @@ -641,12 +639,9 @@ HashCode FuncType::_getHashCodeOverride() // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ExtractExistentialType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String ExtractExistentialType::_toStringOverride() +void ExtractExistentialType::_toTextOverride(StringBuilder& out) { - String result; - result.append(declRef.toString()); - result.append(".This"); - return result; + out << declRef << toSlice(".This"); } bool ExtractExistentialType::_equalsImplOverride(Type* type) @@ -686,20 +681,21 @@ Val* ExtractExistentialType::_substituteImplOverride(ASTBuilder* astBuilder, Sub // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TaggedUnionType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String TaggedUnionType::_toStringOverride() +void TaggedUnionType::_toTextOverride(StringBuilder& out) { - String result; - result.append("__TaggedUnion("); + out << toSlice("__TaggedUnion("); bool first = true; for (auto caseType : caseTypes) { - if (!first) result.append(", "); + if (!first) + { + out << toSlice(", "); + } first = false; - result.append(caseType->toString()); + out << caseType; } - result.append(")"); - return result; + out << toSlice(")"); } bool TaggedUnionType::_equalsImplOverride(Type* type) @@ -764,18 +760,14 @@ Val* TaggedUnionType::_substituteImplOverride(ASTBuilder* astBuilder, Substituti // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ExistentialSpecializedType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String ExistentialSpecializedType::_toStringOverride() +void ExistentialSpecializedType::_toTextOverride(StringBuilder& out) { - String result; - result.append("__ExistentialSpecializedType("); - result.append(baseType->toString()); + out << toSlice("__ExistentialSpecializedType(") << baseType; for (auto arg : args) { - result.append(", "); - result.append(arg.val->toString()); + out << toSlice(", ") << arg.val; } - result.append(")"); - return result; + out << toSlice(")"); } bool ExistentialSpecializedType::_equalsImplOverride(Type * type) @@ -880,12 +872,9 @@ Val* ExistentialSpecializedType::_substituteImplOverride(ASTBuilder* astBuilder, // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ThisType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String ThisType::_toStringOverride() +void ThisType::_toTextOverride(StringBuilder& out) { - String result; - result.append(interfaceDeclRef.toString()); - result.append(".This"); - return result; + out << interfaceDeclRef << toSlice(".This"); } bool ThisType::_equalsImplOverride(Type * type) @@ -940,13 +929,9 @@ Val* ThisType::_substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet s // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AndType !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -String AndType::_toStringOverride() +void AndType::_toTextOverride(StringBuilder& out) { - String result; - result.append(left->toString()); - result.append(" & "); - result.append(right->toString()); - return result; + out << left << toSlice(" & ") << right; } bool AndType::_equalsImplOverride(Type * type) diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index d4d626182..f4aca515c 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -14,7 +14,7 @@ class OverloadGroupType : public Type SLANG_AST_CLASS(OverloadGroupType) // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); Type* _createCanonicalTypeOverride(); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); @@ -28,7 +28,7 @@ class InitializerListType : public Type // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); Type* _createCanonicalTypeOverride(); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); @@ -40,7 +40,7 @@ class ErrorType : public Type SLANG_AST_CLASS(ErrorType) // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); Type* _createCanonicalTypeOverride(); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); @@ -58,7 +58,7 @@ class DeclRefType : public Type static DeclRefType* create(ASTBuilder* astBuilder, DeclRef<Decl> declRef); // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); Type* _createCanonicalTypeOverride(); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); @@ -393,7 +393,7 @@ class ArrayExpressionType : public Type IntVal* arrayLength = nullptr; // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); Type* _createCanonicalTypeOverride(); bool _equalsImplOverride(Type* type); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); @@ -411,7 +411,7 @@ class TypeType : public Type Type* type = nullptr; // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); Type* _createCanonicalTypeOverride(); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); @@ -437,7 +437,7 @@ class VectorExpressionType : public ArithmeticExpressionType IntVal* elementCount = nullptr; // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); BasicExpressionType* _getScalarTypeOverride(); }; @@ -453,7 +453,7 @@ class MatrixExpressionType : public ArithmeticExpressionType Type* getRowType(); // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); BasicExpressionType* _getScalarTypeOverride(); private: @@ -530,7 +530,7 @@ class NamedExpressionType : public Type Type* innerType = nullptr; // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); Type* _createCanonicalTypeOverride(); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); @@ -564,7 +564,7 @@ class FuncType : public Type Type* getResultType() { return resultType; } // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); Type* _createCanonicalTypeOverride(); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); bool _equalsImplOverride(Type* type); @@ -581,7 +581,7 @@ class GenericDeclRefType : public Type DeclRef<GenericDecl> const& getDeclRef() const { return declRef; } // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); Type* _createCanonicalTypeOverride(); @@ -603,7 +603,7 @@ class NamespaceType : public Type DeclRef<NamespaceDeclBase> const& getDeclRef() const { return declRef; } // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); Type* _createCanonicalTypeOverride(); @@ -619,7 +619,7 @@ class ExtractExistentialType : public Type DeclRef<InterfaceDecl> interfaceDeclRef; // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); Type* _createCanonicalTypeOverride(); @@ -639,7 +639,7 @@ class TaggedUnionType : public Type List<Type*> caseTypes; // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); Type* _createCanonicalTypeOverride(); @@ -654,7 +654,7 @@ class ExistentialSpecializedType : public Type ExpandedSpecializationArgs args; // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); Type* _createCanonicalTypeOverride(); @@ -669,7 +669,7 @@ class ThisType : public Type DeclRef<InterfaceDecl> interfaceDeclRef; // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); Type* _createCanonicalTypeOverride(); @@ -687,7 +687,7 @@ class AndType : public Type Type* right; // Overrides should be public so base classes can access - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); bool _equalsImplOverride(Type* type); HashCode _getHashCodeOverride(); Type* _createCanonicalTypeOverride(); diff --git a/source/slang/slang-ast-val.cpp b/source/slang/slang-ast-val.cpp index 5396749e5..97d79e290 100644 --- a/source/slang/slang-ast-val.cpp +++ b/source/slang/slang-ast-val.cpp @@ -26,9 +26,16 @@ bool Val::equalsVal(Val* val) SLANG_AST_NODE_VIRTUAL_CALL(Val, equalsVal, (val)) } +void Val::toText(StringBuilder& out) +{ + SLANG_AST_NODE_VIRTUAL_CALL(Val, toText, (out)) +} + String Val::toString() { - SLANG_AST_NODE_VIRTUAL_CALL(Val, toString, ()) + StringBuilder builder; + toText(builder); + return builder; } HashCode Val::getHashCode() @@ -52,10 +59,10 @@ bool Val::_equalsValOverride(Val* val) //return false; } -String Val::_toStringOverride() +void Val::_toTextOverride(StringBuilder& out) { + SLANG_UNUSED(out); SLANG_UNEXPECTED("Val::_toStringOverride not overridden"); - //return String(); } HashCode Val::_getHashCodeOverride() @@ -73,9 +80,9 @@ bool ConstantIntVal::_equalsValOverride(Val* val) return false; } -String ConstantIntVal::_toStringOverride() +void ConstantIntVal::_toTextOverride(StringBuilder& out) { - return String(value); + out << value; } HashCode ConstantIntVal::_getHashCodeOverride() @@ -94,9 +101,13 @@ bool GenericParamIntVal::_equalsValOverride(Val* val) return false; } -String GenericParamIntVal::_toStringOverride() +void GenericParamIntVal::_toTextOverride(StringBuilder& out) { - return getText(declRef.getName()); + Name* name = declRef.getName(); + if (name) + { + out << name->text; + } } HashCode GenericParamIntVal::_getHashCodeOverride() @@ -157,9 +168,9 @@ bool ErrorIntVal::_equalsValOverride(Val* val) return false; } -String ErrorIntVal::_toStringOverride() +void ErrorIntVal::_toTextOverride(StringBuilder& out) { - return "<error>"; + out << toSlice("<error>"); } HashCode ErrorIntVal::_getHashCodeOverride() @@ -195,9 +206,9 @@ Val* TypeEqualityWitness::_substituteImplOverride(ASTBuilder* astBuilder, Substi return rs; } -String TypeEqualityWitness::_toStringOverride() +void TypeEqualityWitness::_toTextOverride(StringBuilder& out) { - return "TypeEqualityWitness(" + sub->toString() + ")"; + out << toSlice("TypeEqualityWitness(") << sub << toSlice(")"); } HashCode TypeEqualityWitness::_getHashCodeOverride() @@ -319,17 +330,9 @@ Val* DeclaredSubtypeWitness::_substituteImplOverride(ASTBuilder* astBuilder, Sub return rs; } -String DeclaredSubtypeWitness::_toStringOverride() +void DeclaredSubtypeWitness::_toTextOverride(StringBuilder& out) { - StringBuilder sb; - sb << "DeclaredSubtypeWitness("; - sb << this->sub->toString(); - sb << ", "; - sb << this->sup->toString(); - sb << ", "; - sb << this->declRef.toString(); - sb << ")"; - return sb.ProduceString(); + out << toSlice("DeclaredSubtypeWitness(") << sub << toSlice(", ") << sup << toSlice(", ") << declRef << toSlice(")"); } HashCode DeclaredSubtypeWitness::_getHashCodeOverride() @@ -392,18 +395,13 @@ Val* TransitiveSubtypeWitness::_substituteImplOverride(ASTBuilder* astBuilder, S return result; } -String TransitiveSubtypeWitness::_toStringOverride() +void TransitiveSubtypeWitness::_toTextOverride(StringBuilder& out) { // Note: we only print the constituent // witnesses, and rely on them to print // the starting and ending types. - StringBuilder sb; - sb << "TransitiveSubtypeWitness("; - sb << this->subToMid->toString(); - sb << ", "; - sb << this->midToSup->toString(); - sb << ")"; - return sb.ProduceString(); + + out << toSlice("TransitiveSubtypeWitness(") << subToMid << toSlice(", ") << midToSup << toSlice(")"); } HashCode TransitiveSubtypeWitness::_getHashCodeOverride() @@ -426,13 +424,9 @@ bool ExtractExistentialSubtypeWitness::_equalsValOverride(Val* val) return false; } -String ExtractExistentialSubtypeWitness::_toStringOverride() +void ExtractExistentialSubtypeWitness::_toTextOverride(StringBuilder& out) { - String result; - result.append("extractExistentialValue("); - result.append(declRef.toString()); - result.append(")"); - return result; + out << toSlice("extractExistentialValue(") << declRef << toSlice(")"); } HashCode ExtractExistentialSubtypeWitness::_getHashCodeOverride() @@ -482,19 +476,21 @@ bool TaggedUnionSubtypeWitness::_equalsValOverride(Val* val) return true; } -String TaggedUnionSubtypeWitness::_toStringOverride() +void TaggedUnionSubtypeWitness::_toTextOverride(StringBuilder& out) { - String result; - result.append("TaggedUnionSubtypeWitness("); + out << toSlice("TaggedUnionSubtypeWitness("); bool first = true; for (auto caseWitness : caseWitnesses) { - if (!first) result.append(", "); + if (!first) + { + out << toSlice(", "); + } first = false; - result.append(caseWitness->toString()); + out << caseWitness; } - return result; + out << toSlice(")"); } HashCode TaggedUnionSubtypeWitness::_getHashCodeOverride() diff --git a/source/slang/slang-ast-val.h b/source/slang/slang-ast-val.h index dc32b3faa..b76d6325f 100644 --- a/source/slang/slang-ast-val.h +++ b/source/slang/slang-ast-val.h @@ -23,7 +23,7 @@ class ConstantIntVal : public IntVal // Overrides should be public so base classes can access bool _equalsValOverride(Val* val); - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); HashCode _getHashCodeOverride(); protected: @@ -42,7 +42,7 @@ class GenericParamIntVal : public IntVal // Overrides should be public so base classes can access bool _equalsValOverride(Val* val); - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); HashCode _getHashCodeOverride(); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); @@ -63,7 +63,7 @@ class ErrorIntVal : public IntVal // Overrides should be public so base classes can access bool _equalsValOverride(Val* val); - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); HashCode _getHashCodeOverride(); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); }; @@ -125,7 +125,7 @@ class TypeEqualityWitness : public SubtypeWitness // Overrides should be public so base classes can access bool _equalsValOverride(Val* val); - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); HashCode _getHashCodeOverride(); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); }; @@ -140,7 +140,7 @@ class DeclaredSubtypeWitness : public SubtypeWitness // Overrides should be public so base classes can access bool _equalsValOverride(Val* val); - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); HashCode _getHashCodeOverride(); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); }; @@ -158,7 +158,7 @@ class TransitiveSubtypeWitness : public SubtypeWitness // Overrides should be public so base classes can access bool _equalsValOverride(Val* val); - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); HashCode _getHashCodeOverride(); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); }; @@ -174,7 +174,7 @@ class ExtractExistentialSubtypeWitness : public SubtypeWitness // Overrides should be public so base classes can access bool _equalsValOverride(Val* val); - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); HashCode _getHashCodeOverride(); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); }; @@ -195,7 +195,7 @@ class TaggedUnionSubtypeWitness : public SubtypeWitness // Overrides should be public so base classes can access bool _equalsValOverride(Val* val); - String _toStringOverride(); + void _toTextOverride(StringBuilder& out); HashCode _getHashCodeOverride(); Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff); }; diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index 736eee6fb..5c00cb64b 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -13,6 +13,8 @@ #include "slang-lookup.h" +#include "slang-ast-print.h" + namespace Slang { DeclRefType* SemanticsVisitor::getExprDeclRefType(Expr * expr) @@ -518,7 +520,7 @@ namespace Slang for(auto item : lookupResult.items) { - String declString = getDeclSignatureString(item); + String declString = ASTPrinter::getDeclSignatureString(item, m_astBuilder); getSink()->diagnose(item.declRef, Diagnostics::overloadCandidate, declString); } } diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index f1aae6a10..ad9de3f4b 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -1420,22 +1420,6 @@ namespace Slang Expr* funcExpr, OverloadResolveContext& context); - void formatType(StringBuilder& sb, Type* type); - - void formatVal(StringBuilder& sb, Val* val); - - void formatDeclPath(StringBuilder& sb, DeclRef<Decl> declRef); - - void formatDeclParams(StringBuilder& sb, DeclRef<Decl> declRef); - - void formatDeclResultType(StringBuilder& sb, DeclRef<Decl> const& declRef); - - void formatDeclSignature(StringBuilder& sb, DeclRef<Decl> declRef); - - String getDeclSignatureString(DeclRef<Decl> declRef); - - String getDeclSignatureString(LookupResultItem item); - String getCallSignatureString( OverloadResolveContext& context); diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp index a4a55c8ce..c4e4cbde4 100644 --- a/source/slang/slang-check-overload.cpp +++ b/source/slang/slang-check-overload.cpp @@ -2,6 +2,7 @@ #include "slang-check-impl.h" #include "slang-lookup.h" +#include "slang-ast-print.h" // This file implements semantic checking logic related // to resolving overloading call operations, by checking @@ -531,7 +532,7 @@ namespace Slang Diagnostics::genericArgumentInferenceFailed, callString); - String declString = getDeclSignatureString(candidate.item); + String declString = ASTPrinter::getDeclSignatureString(candidate.item, m_astBuilder); getSink()->diagnose(candidate.item.declRef, Diagnostics::genericSignatureTried, declString); goto error; } @@ -1268,197 +1269,6 @@ namespace Slang } } - void SemanticsVisitor::formatType(StringBuilder& sb, Type* type) - { - sb << type->toString(); - } - - void SemanticsVisitor::formatVal(StringBuilder& sb, Val* val) - { - sb << val->toString(); - } - - static void formatDeclName(StringBuilder& sb, Decl* decl) - { - if(as<ConstructorDecl>(decl)) - { - sb << "init"; - } - else if(as<SubscriptDecl>(decl)) - { - sb << "subscript"; - } - else - { - sb << getText(decl->getName()); - } - } - - void SemanticsVisitor::formatDeclPath(StringBuilder& sb, DeclRef<Decl> declRef) - { - // 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<GenericDecl>(); - if(parentGenericDeclRef) - { - parentDeclRef = parentGenericDeclRef.getParent(); - } - - // Depending on what the parent is, we may want to format things specially - if(auto aggTypeDeclRef = parentDeclRef.as<AggTypeDecl>()) - { - formatDeclPath(sb, aggTypeDeclRef); - sb << "."; - } - - formatDeclName(sb, declRef.getDecl()); - - // If the parent declaration is a generic, then we need to print out its - // signature - if( parentGenericDeclRef ) - { - auto genSubst = as<GenericSubstitution>(declRef.substitutions.substitutions); - 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<Witness>(arg)) - continue; - - if(!first) sb << ", "; - formatVal(sb, arg); - first = false; - } - sb << ">"; - } - } - - void SemanticsVisitor::formatDeclParams(StringBuilder& sb, DeclRef<Decl> declRef) - { - if (auto funcDeclRef = declRef.as<CallableDecl>()) - { - - // 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 << ", "; - - formatType(sb, getType(m_astBuilder, paramDeclRef)); - - first = false; - - } - - sb << ")"; - } - else if(auto genericDeclRef = declRef.as<GenericDecl>()) - { - sb << "<"; - bool first = true; - for (auto paramDeclRef : getMembers(genericDeclRef)) - { - if(auto genericTypeParam = paramDeclRef.as<GenericTypeParamDecl>()) - { - if (!first) sb << ", "; - first = false; - - sb << getText(genericTypeParam.getName()); - } - else if(auto genericValParam = paramDeclRef.as<GenericValueParamDecl>()) - { - if (!first) sb << ", "; - first = false; - - sb << getText(genericValParam.getName()); - sb << ":"; - formatType(sb, getType(m_astBuilder, genericValParam)); - } - else - {} - } - sb << ">"; - - formatDeclParams(sb, DeclRef<Decl>(getInner(genericDeclRef), genericDeclRef.substitutions)); - } - else - { - } - } - - static void formatDeclKindPrefix(StringBuilder& sb, Decl* decl) - { - if(auto genericDecl = as<GenericDecl>(decl)) - { - decl = genericDecl->inner; - } - if(as<FuncDecl>(decl)) - { - sb << "func "; - } - } - - void SemanticsVisitor::formatDeclResultType(StringBuilder& sb, DeclRef<Decl> const& inDeclRef) - { - DeclRef<Decl> declRef = inDeclRef; - if(auto genericDeclRef = declRef.as<GenericDecl>()) - { - declRef = DeclRef<Decl>(getInner(genericDeclRef), genericDeclRef.substitutions); - } - - if(as<ConstructorDecl>(declRef)) - {} - else if(auto callableDeclRef = declRef.as<CallableDecl>()) - { - sb << " -> "; - formatType(sb, getResultType(m_astBuilder, callableDeclRef)); - } - } - - void SemanticsVisitor::formatDeclSignature(StringBuilder& sb, DeclRef<Decl> declRef) - { - formatDeclKindPrefix(sb, declRef.getDecl()); - formatDeclPath(sb, declRef); - formatDeclParams(sb, declRef); - formatDeclResultType(sb, declRef); - } - - String SemanticsVisitor::getDeclSignatureString(DeclRef<Decl> declRef) - { - StringBuilder sb; - formatDeclSignature(sb, declRef); - return sb.ProduceString(); - } - - String SemanticsVisitor::getDeclSignatureString(LookupResultItem item) - { - return getDeclSignatureString(item.declRef); - } - String SemanticsVisitor::getCallSignatureString( OverloadResolveContext& context) { @@ -1469,7 +1279,7 @@ namespace Slang for( UInt aa = 0; aa < argCount; ++aa ) { if(aa != 0) argsListBuilder << ", "; - argsListBuilder << context.getArgType(aa)->toString(); + context.getArgType(aa)->toText(argsListBuilder); } argsListBuilder << ")"; return argsListBuilder.ProduceString(); @@ -1632,7 +1442,7 @@ namespace Slang Index candidateIndex = 0; for (auto candidate : context.bestCandidates) { - String declString = getDeclSignatureString(candidate.item); + String declString = ASTPrinter::getDeclSignatureString(candidate.item, m_astBuilder); // declString = declString + "[" + String(candidate.conversionCostSum) + "]"; diff --git a/source/slang/slang-doc.cpp b/source/slang/slang-doc.cpp index fa3e11030..c72250122 100644 --- a/source/slang/slang-doc.cpp +++ b/source/slang/slang-doc.cpp @@ -3,6 +3,9 @@ #include "../core/slang-string-util.h" +#include "slang-ast-builder.h" +#include "slang-ast-print.h" + namespace Slang { /* TODO(JS): @@ -33,19 +36,23 @@ public: }; }; + // NOTE! Don't change order without fixing isBefore and isAfter enum class MarkupType { None, - BlockBefore, /// /** */ or /*! */. - BlockAfter, /// /*!< */ or /**< */ + BlockBefore, /// /** */ or /*! */. LineBangBefore, /// //! Can be multiple lines LineSlashBefore, /// /// Can be multiple lines + BlockAfter, /// /*!< */ or /**< */ LineBangAfter, /// //!< Can be multiple lines LineSlashAfter, /// ///< Can be multiple lines }; + static bool isBefore(MarkupType type) { return Index(type) >= Index(MarkupType::BlockBefore) && Index(type) <= Index(MarkupType::LineSlashBefore); } + static bool isAfter(MarkupType type) { return Index(type) >= Index(MarkupType::BlockAfter); } + struct IndexRange { SLANG_FORCE_INLINE Index getCount() const { return end - start; } @@ -60,8 +67,12 @@ public: Before, AfterParam, ///< Can have trailing , or ) AfterSemicolon, ///< Can have a trailing ; + AfterEnumCase, ///< Can have a , or before } }; + static bool isAfter(Location location) { return Index(location) >= Index(Location::AfterParam); } + static bool isBefore(Location location) { return location == Location::Before; } + struct FoundMarkup { void reset() @@ -199,6 +210,7 @@ void DocMarkupExtractor::_addDeclRec(Decl* decl) } else #endif + if (ContainerDecl* containerDecl = as<ContainerDecl>(decl)) { // Add the container - which could be a class, struct, enum, namespace, extension, generic etc. @@ -465,7 +477,7 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio const TokenList& toks = *info.tokenList; const Index tokIndex = info.declTokenIndex; - Index direction = (location == Location::Before) ? -1 : 1; + Index direction = isBefore(location) ? -1 : 1; const Index count = toks.m_tokens.getCount(); for (Index i = tokIndex; i >= 0 && i < count; i += direction) @@ -474,6 +486,26 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio switch (tok.type) { + case TokenType::BlockComment: + case TokenType::LineComment: + { + if (openParensCount == 0 && openBracketCount == 0) + { + // Determine the markup type + const MarkupType markupType = findMarkupType(tok); + // If the location wanted is before and the markup is, we'll assume this is it + if (isBefore(location) && isBefore(markupType)) + { + return i; + } + // If we are looking for enum cases, and the markup is after, we'll assume this is it + if (location == Location::AfterEnumCase && isAfter(markupType)) + { + return i; + } + } + break; + } case TokenType::LParent: { ++openParensCount; @@ -481,12 +513,12 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio } case TokenType::RBracket: { - openBracketCount += Index(location == Location::Before); + openBracketCount += Index(isBefore(location)); break; } case TokenType::LBracket: { - openBracketCount -= Index(location == Location::Before); + openBracketCount -= Index(isBefore(location)); break; } case TokenType::RParent: @@ -507,7 +539,7 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio } case TokenType::Comma: { - if (location == Location::AfterParam) + if (location == Location::AfterParam || location == Location::AfterEnumCase) { return i + 1; } @@ -516,7 +548,7 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio case TokenType::RBrace: { // If we haven't hit a candidate yet before hitting } it's not going to work - if (location == Location::Before) + if (location == Location::Before || location == Location::AfterEnumCase) { return -1; } @@ -536,16 +568,6 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio } break; } - case TokenType::LineComment: - case TokenType::BlockComment: - { - // We hit a comment this could be the markup - if (location == Location::Before && openParensCount == 0 && openBracketCount == 0) - { - return i; - } - break; - } default: break; } } @@ -600,13 +622,13 @@ SlangResult DocMarkupExtractor::_findMarkup(const FindInfo& info, Location locat return SLANG_E_NOT_FOUND; } - const Index searchDirection = (location == Location::Before) ? -1 : 1; + const Index searchDirection = isBefore(location) ? -1 : 1; // Get the type and flags const MarkupType type = findMarkupType(toks[startIndex]); const MarkupFlags flags = getFlags(type); - const MarkupFlag::Enum requiredFlag = (location == Location::Before) ? MarkupFlag::Before : MarkupFlag::After; + const MarkupFlag::Enum requiredFlag = isBefore(location) ? MarkupFlag::Before : MarkupFlag::After; if ((flags & requiredFlag) == 0) { return SLANG_E_NOT_FOUND; @@ -714,6 +736,11 @@ SlangResult DocMarkupExtractor::_findMarkup(const FindInfo& info, const Location SlangResult DocMarkupExtractor::_findMarkup(const FindInfo& info, Decl* decl, FoundMarkup& out) { + if (auto enumCaseDecl = as<EnumCaseDecl>(decl)) + { + Location locs[] = { Location::Before, Location::AfterEnumCase }; + return _findMarkup(info, locs, SLANG_COUNT_OF(locs), out); + } if (auto paramDecl = as<ParamDecl>(decl)) { Location locs[] = { Location::Before, Location::AfterParam }; @@ -901,72 +928,320 @@ SlangResult DocMarkup::extract(ModuleDecl* moduleDecl, SourceManager* sourceMana return context.extract(this, moduleDecl, sourceManager, sink); } -/* static */SlangResult DocumentationUtil::writeMarkdown(DocMarkup* markup, StringBuilder& out) +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DocMarkDownWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +struct DocMarkDownWriter { - for (const auto& entry : markup->getEntries()) + typedef ASTPrinter::Part Part; + typedef ASTPrinter::PartPair PartPair; + + struct Signature { - NodeBase* node = entry.m_node; - Decl* decl = as<Decl>(node); - if (!decl) + Part returnType; + List<PartPair> params; + Part name; + }; + + void write(); + + void writeCallable(const DocMarkup::Entry& entry, CallableDecl* callable); + void writeEnum(const DocMarkup::Entry& entry, EnumDecl* enumDecl); + void writeAggType(const DocMarkup::Entry& entry, AggTypeDecl* aggTypeDecl); + + void writePreamble(const DocMarkup::Entry& entry); + void writeDescription(const DocMarkup::Entry& entry); + + DocMarkDownWriter(DocMarkup* markup, ASTBuilder* astBuilder): + m_markup(markup), + m_astBuilder(astBuilder) + { + } + + static void getSignature(const List<Part>& parts, Signature& outSig); + + template <typename T> + void _appendAsBullets(FilteredMemberList<T>& in); + + DocMarkup* m_markup; + ASTBuilder* m_astBuilder; + StringBuilder m_builder; +}; + +static void _appendAsSingleLine(const UnownedStringSlice& in, StringBuilder& out) +{ + List<UnownedStringSlice> lines; + StringUtil::calcLines(in, lines); + + // Ideally we'd remove any extraneous whitespace, but for now just join + StringUtil::join(lines.getBuffer(), lines.getCount(), ' ', out); +} + +template <typename T> +void DocMarkDownWriter::_appendAsBullets(FilteredMemberList<T>& list) +{ + auto& out = m_builder; + for (auto element : list) + { + DocMarkup::Entry* paramEntry = m_markup->getEntry(element); + + out << "* "; + + Name* name = element->getName(); + if (name) { - continue; + out << toSlice("_") << name->text << toSlice("_ "); } - // Skip these they will be output as part of their respective 'containers' - if (as<ParamDecl>(decl) || as<EnumCaseDecl>(decl)) + if (paramEntry) { - continue; + // Hmm, we'll want to make something multiline into a single line + _appendAsSingleLine(paramEntry->m_markup.getUnownedSlice(), out); } - if (CallableDecl* callableDecl = as<CallableDecl>(decl)) + out << "\n"; + } + + out << toSlice("\n"); +} + +/* static */void DocMarkDownWriter::getSignature(const List<Part>& parts, Signature& outSig) +{ + const Index count = parts.getCount(); + for (Index i = 0; i < count; ++i) + { + const auto& part = parts[i]; + switch (part.type) { - out << entry.m_markup; + case Part::Type::ParamType: + { + PartPair pair; + pair.first = part; + if (parts[i + 1].type == Part::Type::ParamName) + { + pair.second = parts[i + 1]; + i++; + } + outSig.params.add(pair); + break; + } + case Part::Type::ReturnType: + { + outSig.returnType = part; + break; + } + case Part::Type::DeclPath: + { + outSig.name = part; + break; + } + default: break; + } + } +} - // There's code to output sigs in the SemanticsVisitor - we probably need to extract that functionality - // out so can be used here +void DocMarkDownWriter::writeCallable(const DocMarkup::Entry& entry, CallableDecl* callableDecl) +{ + writePreamble(entry); - // String declString = getDeclSignatureString(item); + auto& out = m_builder; - auto params = callableDecl->getParameters(); - //const auto& returnType = callableDecl->returnType; + StringBuilder sigBuffer; + List<ASTPrinter::Part> parts; + ASTPrinter printer(m_astBuilder, ASTPrinter::OptionFlag::ParamNames, &parts); - // Let's see if we can get markup on the parameters - for (auto param : params) - { - DocMarkup::Entry* paramEntry = markup->getEntry(param); + printer.addDeclSignature(DeclRef<Decl>(callableDecl, nullptr)); - if (paramEntry) - { - out << paramEntry->m_markup; + Signature signature; + getSignature(parts, signature); - auto type = param->getType(); + const Index paramCount = signature.params.getCount(); - if (type) - { - out << type->toString(); - } + // Output the signature + { + // Extract the name + out << toSlice("# ") << printer.getPartSlice(signature.name) << toSlice("\n\n"); - Name* name = param->getName(); - if (name) - { - out << " "; - out << name->text; - } - out << "\n\n"; + out << toSlice("## Signature \n"); + out << toSlice("```\n"); + out << printer.getPartSlice(signature.returnType) << toSlice(" "); + + out << printer.getPartSlice(signature.name); + + + if (paramCount > 0) + { + out << toSlice("(\n"); + + StringBuilder line; + for (Index i = 0; i < paramCount; ++i) + { + const auto& param = signature.params[i]; + line.Clear(); + // If we want to tab these over... we'll need to know how must space I have + line << " " << printer.getPartSlice(param.first); + + Index indent = 25; + if (line.getLength() < indent) + { + line.appendRepeatedChar(' ', indent - line.getLength()); + } + else + { + line.appendChar(' '); } + + line << printer.getPartSlice(param.second); + if (i < paramCount - 1) + { + line << ",\n"; + } + + out << line; } + + out << ");\n"; } - else if (EnumDecl* enumDecl = as<EnumDecl>(decl)) + else + { + out << toSlice("();\n"); + } + + out << "```\n\n"; + } + + // Only output params if there are any + if (paramCount) + { + out << "## Parameters\n\n"; + + auto params = callableDecl->getParameters(); + _appendAsBullets(params); + } + + writeDescription(entry); +} + +void DocMarkDownWriter::writeEnum(const DocMarkup::Entry& entry, EnumDecl* enumDecl) +{ + writePreamble(entry); + + auto& out = m_builder; + + out << toSlice("# enum "); + Name* name = enumDecl->getName(); + if (name) + { + out << name->text; + } + out << toSlice("\n\n"); + + out << toSlice("## Values \n\n"); + + auto cases = enumDecl->getMembersOfType<EnumCaseDecl>(); + _appendAsBullets(cases); + + writeDescription(entry); +} + +void DocMarkDownWriter::writeAggType(const DocMarkup::Entry& entry, AggTypeDecl* aggTypeDecl) +{ + writePreamble(entry); + + auto& out = m_builder; + + // This could be lots of different things - struct/class/extension/interface/.. + + out << toSlice("# "); + if (as<StructDecl>(aggTypeDecl)) + { + out << toSlice("struct "); + } + else if (as<ClassDecl>(aggTypeDecl)) + { + out << toSlice("class "); + } + else + { + out << toSlice("?"); + } + + Name* name = aggTypeDecl->getName(); + if (name) + { + out << name->text; + } + out << toSlice("\n\n"); + + out << "## Fields\n\n"; + + auto fields = aggTypeDecl->getMembersOfType<VarDecl>(); + _appendAsBullets(fields); + + writeDescription(entry); +} + +void DocMarkDownWriter::writePreamble(const DocMarkup::Entry& entry) +{ + SLANG_UNUSED(entry); + auto& out = m_builder; + + out << toSlice("\n"); + out.appendRepeatedChar('-', 80); + out << toSlice("\n"); +} + + +void DocMarkDownWriter::writeDescription(const DocMarkup::Entry& entry) +{ + auto& out = m_builder; + + out << toSlice("\n## Description\n\n"); + out << entry.m_markup; +} + +void DocMarkDownWriter::write() +{ + for (const auto& entry : m_markup->getEntries()) + { + NodeBase* node = entry.m_node; + Decl* decl = as<Decl>(node); + if (!decl) + { + continue; + } + + // Skip these they will be output as part of their respective 'containers' + if (as<ParamDecl>(decl) || as<EnumCaseDecl>(decl)) { + continue; + } + if (CallableDecl* callableDecl = as<CallableDecl>(decl)) + { + writeCallable(entry, callableDecl); } - else if (StructDecl* structDecl = as<StructDecl>(decl)) + else if (EnumDecl* enumDecl = as<EnumDecl>(decl)) { + writeEnum(entry, enumDecl); } - else if (ClassDecl* classDecl = as<ClassDecl>(decl)) + else if (AggTypeDecl* aggType = as<AggTypeDecl>(decl)) { + writeAggType(entry, aggType); } } +} + +/* static */SlangResult DocumentationUtil::writeMarkdown(DocMarkup* markup, ASTBuilder* astBuilder, StringBuilder& out) +{ + // The ASTBuilder is needed in order to be able to create ast types that can then be printed. + // It is *assumed* here, that them being transient on this temporary ASTBuilder, doesn't mutate + // any of the nodes from the ASTBuilder/s for the things being documented + + DocMarkDownWriter writer(markup, astBuilder); + writer.write(); + + Swap(out, writer.m_builder); return SLANG_OK; } diff --git a/source/slang/slang-doc.h b/source/slang/slang-doc.h index 49251808a..b2fd8c664 100644 --- a/source/slang/slang-doc.h +++ b/source/slang/slang-doc.h @@ -4,6 +4,7 @@ #include "../core/slang-basic.h" #include "slang-ast-all.h" +#include "slang-ast-print.h" namespace Slang { @@ -60,9 +61,11 @@ SLANG_INLINE DocMarkup::Entry* DocMarkup::getEntry(NodeBase* base) return (indexPtr) ? &m_entries[*indexPtr] : nullptr; } +class SharedASTBuilder; + struct DocumentationUtil { - static SlangResult writeMarkdown(DocMarkup* markup, StringBuilder& out); + static SlangResult writeMarkdown(DocMarkup* markup, ASTBuilder* astBuilder, StringBuilder& out); }; } // namespace Slang diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 395ea1ad1..bca493780 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -3620,6 +3620,8 @@ namespace Slang static EnumCaseDecl* parseEnumCaseDecl(Parser* parser) { EnumCaseDecl* decl = parser->astBuilder->create<EnumCaseDecl>(); + parser->FillPosition(decl); + decl->nameAndLoc = expectIdentifier(parser); if(AdvanceIf(parser, TokenType::OpAssign)) diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp index d6e966aaa..62de6599c 100644 --- a/source/slang/slang-syntax.cpp +++ b/source/slang/slang-syntax.cpp @@ -20,23 +20,23 @@ void printDiagnosticArg(StringBuilder& sb, Decl* decl) void printDiagnosticArg(StringBuilder& sb, Type* type) { - sb << type->toString(); + type->toText(sb); } void printDiagnosticArg(StringBuilder& sb, Val* val) { - sb << val->toString(); + val->toText(sb); } void printDiagnosticArg(StringBuilder& sb, TypeExp const& type) { - sb << type.type->toString(); + type.type->toText(sb); } void printDiagnosticArg(StringBuilder& sb, QualType const& type) { if (type.type) - sb << type.type->toString(); + type.type->toText(sb); else sb << "<null>"; } @@ -1104,13 +1104,22 @@ Index getFilterCountImpl(const ReflectClassInfo& clsInfo, MemberFilterStyle filt String DeclRefBase::toString() const { - if (!decl) return ""; - - auto name = decl->getName(); - if (!name) return ""; + StringBuilder builder; + toText(builder); + return builder; + } - // TODO: need to print out substitutions too! - return name->text; + void DeclRefBase::toText(StringBuilder& out) const + { + if (decl) + { + auto name = decl->getName(); + if (name) + { + // TODO: need to print out substitutions too! + out << name->text; + } + } } bool SubstitutionSet::equals(const SubstitutionSet& substSet) const diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index e02715015..21f60c090 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -1639,25 +1639,7 @@ void FrontEndCompileRequest::parseTranslationUnit( } } - if (shouldDocument) - { - RefPtr<DocMarkup> markup(new DocMarkup); - markup->extract(translationUnit->getModuleDecl(), getSourceManager(), getSink()); - - // Extract to a file - const String& path = sourceFile->getPathInfo().foundPath; - if (path.getLength()) - { - String fileName = Path::getFileNameWithoutExt(path); - fileName.append(".md"); - - StringBuilder buf; - DocumentationUtil::writeMarkdown(markup, buf); - - File::writeAllText(fileName, buf); - } - } #if 0 // Test serialization @@ -1816,6 +1798,38 @@ SlangResult FrontEndCompileRequest::executeActionsInner() if (getSink()->getErrorCount() != 0) return SLANG_FAIL; + // After semantic checking is performed we can try and output doc information for this + if (shouldDocument) + { + // Not 100% clear where best to get the ASTBuilder from, but from the linkage shouldn't + // cause any problems with scoping + ASTBuilder* astBuilder = getLinkage()->getASTBuilder(); + + for (TranslationUnitRequest* translationUnit : translationUnits) + { + RefPtr<DocMarkup> markup(new DocMarkup); + + markup->extract(translationUnit->getModuleDecl(), getSourceManager(), getSink()); + + // Hmm.. we can have multiple sourcefiles. So fir now we just pick the first, so as to come up with + // a reasonable name + SourceFile* sourceFile = translationUnit->getSourceFiles()[0]; + + // Extract to a file + const String& path = sourceFile->getPathInfo().foundPath; + if (path.getLength()) + { + String fileName = Path::getFileNameWithoutExt(path); + fileName.append(".md"); + + StringBuilder buf; + DocumentationUtil::writeMarkdown(markup, astBuilder, buf); + + File::writeAllText(fileName, buf); + } + } + } + // Look up all the entry points that are expected, // and use them to populate the `program` member. // |
