-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added DocumentationTypeReference+DocumentationTypeNode and their pars…
…ing.
- Loading branch information
1 parent
b3554a7
commit c81b5b3
Showing
14 changed files
with
354 additions
and
155 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
|
||
extension DocumentationTypeNode { | ||
public init(parsing str: String) throws { | ||
var remainder = Substring(str) | ||
self = try Self(consuming: &remainder) | ||
guard remainder.isEmpty else { throw DocumentationFormatError() } | ||
} | ||
|
||
internal init(consuming str: inout Substring) throws { | ||
self = try Self.consume(&str) | ||
} | ||
|
||
internal static func consume(_ remainder: inout Substring) throws -> Self { | ||
var typeNode: Self | ||
if remainder.tryConsume("`") { | ||
let kind = remainder.tryConsume("`") ? GenericArgKind.method : .type | ||
let digits = remainder.consume(while: { $0.isNumber }) | ||
guard let index = Int(digits), index > 0 else { throw DocumentationFormatError() } | ||
typeNode = .genericArg(index: index, kind: kind) | ||
} | ||
else { | ||
let typeReference = try DocumentationTypeReference(consuming: &remainder) | ||
guard case .bound = typeReference.genericity else { throw DocumentationFormatError() } | ||
typeNode = .bound(typeReference) | ||
} | ||
|
||
while true { | ||
if remainder.tryConsume("[") { | ||
guard remainder.tryConsume("]") else { throw DocumentationFormatError() } | ||
typeNode = .array(of: typeNode) | ||
} | ||
else if remainder.tryConsume("*") { | ||
typeNode = .pointer(to: typeNode) | ||
} | ||
else { | ||
break | ||
} | ||
} | ||
|
||
return typeNode | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
public enum DocumentationTypeNode: Hashable { | ||
case bound(DocumentationTypeReference) | ||
indirect case array(of: DocumentationTypeNode) | ||
indirect case pointer(to: DocumentationTypeNode) | ||
case genericArg(index: Int, kind: GenericArgKind) | ||
|
||
public enum GenericArgKind: Hashable { | ||
case type | ||
case method | ||
} | ||
} | ||
|
||
extension DocumentationTypeNode { | ||
public static func bound( | ||
namespace: String? = nil, | ||
nameWithoutGenericSuffix: String, | ||
genericity: DocumentationTypeReference.Genericity = .bound([])) -> Self { | ||
.bound(DocumentationTypeReference( | ||
namespace: namespace, | ||
nameWithoutGenericSuffix: nameWithoutGenericSuffix, | ||
genericity: genericity)) | ||
} | ||
} |
86 changes: 86 additions & 0 deletions
86
Sources/DotNetXMLDocs/DocumentationTypeReference+parsing.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
extension DocumentationTypeReference { | ||
public init(parsing str: String) throws { | ||
var remainder = Substring(str) | ||
self = try Self(consuming: &remainder) | ||
guard remainder.isEmpty else { throw DocumentationFormatError() } | ||
} | ||
|
||
internal init(consuming str: inout Substring, ignoreMemberSuffix: Bool = false) throws { | ||
self = try Self.consume(&str, ignoreMemberSuffix: ignoreMemberSuffix) | ||
} | ||
|
||
internal static func consume(_ remainder: inout Substring, ignoreMemberSuffix: Bool = false) throws -> Self { | ||
let identifier = try consumeNamespaceAndName(&remainder, ignoreMemberSuffix: ignoreMemberSuffix) | ||
let genericity = try consumeGenericity(&remainder) | ||
|
||
return Self( | ||
namespace: identifier.namespace.map(String.init), | ||
nameWithoutGenericSuffix: String(identifier.name), | ||
genericity: genericity) | ||
} | ||
|
||
internal static func consumeNamespaceAndName( | ||
_ remainder: inout Substring, | ||
ignoreMemberSuffix: Bool = false) throws -> (namespace: Substring?, name: Substring) { | ||
let original = remainder | ||
var name = try consumeIdentifier(&remainder) | ||
var namespace: Substring? = nil | ||
while true { | ||
let preDot = remainder | ||
guard remainder.tryConsume(".") else { break } | ||
let newName: Substring | ||
if ignoreMemberSuffix { | ||
if let possibleName = try? consumeIdentifier(&remainder), | ||
remainder.first == "." { | ||
newName = possibleName | ||
} | ||
else { | ||
remainder = preDot | ||
break | ||
} | ||
} | ||
else { | ||
newName = try consumeIdentifier(&remainder) | ||
} | ||
|
||
namespace = original[..<preDot.startIndex] | ||
name = newName | ||
} | ||
|
||
return (namespace, name) | ||
} | ||
|
||
internal static func consumeGenericity(_ remainder: inout Substring) throws -> Genericity { | ||
// Parse generic arity such as `1 | ||
let genericArity: Int | ||
if remainder.tryConsume("`") { | ||
let digits = remainder.consume(while: { $0.isNumber }) | ||
guard let value = Int(digits), value > 0 else { throw DocumentationFormatError() } | ||
genericArity = value | ||
} | ||
else { | ||
genericArity = 0 | ||
} | ||
|
||
// Parse bound generic args such as {System.String} | ||
let genericArgs: [DocumentationTypeNode]? = try { | ||
guard remainder.tryConsume("{") else { return nil } | ||
|
||
var genericArgs: [DocumentationTypeNode] = [] | ||
while !remainder.tryConsume("}") { | ||
if !genericArgs.isEmpty && !remainder.tryConsume(",") { throw DocumentationFormatError() } | ||
genericArgs.append(try DocumentationTypeNode(consuming: &remainder)) | ||
} | ||
|
||
return genericArgs | ||
}() | ||
|
||
if let genericArgs { | ||
guard genericArity == genericArgs.count else { throw DocumentationFormatError() } | ||
return .bound(genericArgs) | ||
} | ||
else { | ||
return genericArity == 0 ? .bound([]) : .unbound(arity: genericArity) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
public struct DocumentationTypeReference: Hashable { | ||
public var namespace: String? | ||
public var nameWithoutGenericSuffix: String | ||
public var genericity: Genericity | ||
|
||
public init(namespace: String? = nil, nameWithoutGenericSuffix: String, genericity: Genericity = .bound([])) { | ||
self.namespace = namespace | ||
self.nameWithoutGenericSuffix = nameWithoutGenericSuffix | ||
self.genericity = genericity | ||
} | ||
|
||
public init(namespace: String? = nil, nameWithoutGenericSuffix: String, genericArity: Int) { | ||
self.init( | ||
namespace: namespace, | ||
nameWithoutGenericSuffix: nameWithoutGenericSuffix, | ||
genericity: genericArity == 0 ? .bound([]) : .unbound(arity: genericArity)) | ||
} | ||
|
||
public init(namespace: String? = nil, nameWithoutGenericSuffix: String, genericArgs: [DocumentationTypeNode]) { | ||
self.init( | ||
namespace: namespace, | ||
nameWithoutGenericSuffix: nameWithoutGenericSuffix, | ||
genericity: .bound(genericArgs)) | ||
} | ||
|
||
public var genericArity: Int { | ||
switch genericity { | ||
case .unbound(let arity): return arity | ||
case .bound(let args): return args.count | ||
} | ||
} | ||
|
||
public enum Genericity: Hashable { | ||
case unbound(arity: Int) // System.Action`1, arity should not be zero | ||
case bound([DocumentationTypeNode]) // System.String or System.Action`1{System.String} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.