Skip to content

Eliminate SyntaxNode #684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 24 additions & 32 deletions Sources/SwiftSyntax/IncrementalParseTransition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,18 @@ public protocol IncrementalParseReusedNodeDelegate {
/// - range: The source region of the currently parsed source.
/// - previousNode: The node from the previous tree that is associated with
/// the skipped source region.
func parserReusedNode(range: ByteSourceRange, previousNode: SyntaxNode)
func parserReusedNode(range: ByteSourceRange, previousNode: Syntax)
}

/// An implementation of `IncrementalParseReusedNodeDelegate` that just collects
/// the range and re-used node into an array.
public final class IncrementalParseReusedNodeCollector:
IncrementalParseReusedNodeDelegate {
public var rangeAndNodes: [(ByteSourceRange, SyntaxNode)] = []
public var rangeAndNodes: [(ByteSourceRange, Syntax)] = []

public init() {}

public func parserReusedNode(range: ByteSourceRange, previousNode: SyntaxNode) {
public func parserReusedNode(range: ByteSourceRange, previousNode: Syntax) {
rangeAndNodes.append((range, previousNode))
}
}
Expand Down Expand Up @@ -226,7 +226,7 @@ public struct IncrementalParseLookup {

public init(transition: IncrementalParseTransition) {
self.transition = transition
self.cursor = .init(root: transition.previousTree.data.absoluteRaw)
self.cursor = .init(root: transition.previousTree.data)
}

fileprivate var edits: ConcurrentEdits {
Expand All @@ -246,11 +246,11 @@ public struct IncrementalParseLookup {
/// - Parameters:
/// - offset: The byte offset of the source string that is currently parsed.
/// - kind: The `CSyntaxKind` that the parser expects at this position.
/// - Returns: A `SyntaxNode` node from the previous parse invocation,
/// - Returns: A `Syntax` node from the previous parse invocation,
/// representing the contents of this region, if it is still valid
/// to re-use. `nil` otherwise.
@_spi(RawSyntax)
public mutating func lookUp(_ newOffset: Int, kind: SyntaxKind) -> SyntaxNode? {
public mutating func lookUp(_ newOffset: Int, kind: SyntaxKind) -> Syntax? {
guard let prevOffset = translateToPreEditOffset(newOffset) else {
return nil
}
Expand All @@ -266,7 +266,7 @@ public struct IncrementalParseLookup {

mutating fileprivate func cursorLookup(
prevPosition: AbsolutePosition, kind: SyntaxKind
) -> SyntaxNode? {
) -> Syntax? {
guard !cursor.finished else { return nil }

while true {
Expand Down Expand Up @@ -348,60 +348,52 @@ public struct IncrementalParseLookup {
/// Functions as an iterator that walks the tree looking for nodes with a
/// certain position.
fileprivate struct SyntaxCursor {
var parents: [AbsoluteRawSyntax]
var node: AbsoluteRawSyntax
var node: SyntaxData
var finished: Bool
let viewMode = SyntaxTreeViewMode.sourceAccurate

init(root: AbsoluteRawSyntax) {
init(root: SyntaxData) {
self.node = root
self.parents = []
self.finished = false
}

var asSyntaxNode: SyntaxNode {
return SyntaxNode(node: node, parents: ArraySlice(parents))
var asSyntaxNode: Syntax {
return Syntax(node)
}

/// Returns the next sibling node or the parent's sibling node if this is
/// the last child. The cursor state is unmodified.
/// - Returns: False if it run out of nodes to walk to.
var nextSibling: AbsoluteRawSyntax? {
var parents = ArraySlice(self.parents)
/// - Returns: `nil` if it run out of nodes to walk to.
var nextSibling: SyntaxData? {
var node = self.node
while !parents.isEmpty {
if let sibling = node.nextSibling(parent: parents.last!, viewMode: viewMode) {
return sibling
while let parent = node.parent {
if let sibling = node.absoluteRaw.nextSibling(parent: parent.absoluteRaw, viewMode: viewMode) {
return SyntaxData(sibling, parent: Syntax(parent))
}
node = parents.removeLast()
node = parent
}

return nil
}

/// Moves to the first child of the current node.
/// - Returns: False if the node has no children.
mutating func advanceToFirstChild() -> Bool {
guard let child = node.firstChild(viewMode: viewMode) else { return false }
parents.append(node)
node = child
guard let child = node.absoluteRaw.firstChild(viewMode: viewMode) else { return false }
node = SyntaxData(child, parent: Syntax(node))
return true
}

/// Moves to the next sibling node or the parent's sibling node if this is
/// the last child.
/// - Returns: False if it run out of nodes to walk to.
mutating func advanceToNextSibling() -> Bool {
while !parents.isEmpty {
if let sibling = node.nextSibling(parent: parents.last!, viewMode: viewMode) {
node = sibling
return true
}
node = parents.removeLast()
guard let next = nextSibling else {
finished = true
return false
}

finished = true
return false
self.node = next
return true
}

/// Moves to the next node in the tree with the provided `position`.
Expand Down
20 changes: 1 addition & 19 deletions Sources/SwiftSyntax/Misc.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
NODE_MAP = create_node_map()
# Ignore the following admonition it applies to the resulting .swift file only
}%
//// Automatically Generated From SyntaxNodes.swift.gyb.
//// Automatically Generated From Misc.swift.gyb.
//// Do Not Edit Directly!
//===---------- Misc.swift - Miscelaneous SwiftSyntax definitions ---------===//
//
Expand All @@ -19,24 +19,6 @@
//
//===----------------------------------------------------------------------===//

extension SyntaxNode {
public var isUnknown: Bool { return raw.kind.isUnknown }
public var asUnknown: UnknownSyntax? {
guard isUnknown else { return nil }
return UnknownSyntax(asSyntaxData)
}
% for node in SYNTAX_NODES:
% if not node.is_base():

public var is${node.syntax_kind}: Bool { return raw.kind == .${node.swift_syntax_kind} }
public var as${node.syntax_kind}: ${node.name}? {
guard is${node.syntax_kind} else { return nil }
return ${node.name}(asSyntaxData)
}
% end
% end
}

extension Syntax {
/// Syntax nodes always conform to SyntaxProtocol. This API is just added
/// for consistency.
Expand Down
106 changes: 3 additions & 103 deletions Sources/SwiftSyntax/Syntax.swift
Original file line number Diff line number Diff line change
Expand Up @@ -655,109 +655,6 @@ extension ReversedTokenSequence: CustomReflectable {
}
}

/// Represents a node from the syntax tree.
///
/// This is a more efficient representation than `Syntax` because it avoids casts
/// to `Syntax` for representing the parent hierarchy.
/// It provides generic information, like the node's position, range, and
/// a unique `id`, while still allowing getting the associated `Syntax`
/// object if necessary.
///
/// `SyntaxParser` uses `SyntaxNode` to efficiently report which syntax nodes
/// got re-used during incremental re-parsing.
public struct SyntaxNode {
let absoluteRaw: AbsoluteRawSyntax
let parents: ArraySlice<AbsoluteRawSyntax>

internal init(node: AbsoluteRawSyntax, parents: ArraySlice<AbsoluteRawSyntax>) {
self.absoluteRaw = node
self.parents = parents
}

var raw: RawSyntax {
return absoluteRaw.raw
}

@_spi(RawSyntax)
public func withUnsafeRawSyntax<R>(_ body: (RawSyntax) throws -> R) rethrows -> R {
return try body(raw)
}

/// Converts this node to a `SyntaxData` object.
///
/// This operation results in wrapping all of the node's parents into
/// `SyntaxData` objects. There's a cost associated with it that should be
/// taken into account before used inside performance critical code.
internal var asSyntaxData: SyntaxData {
if let parent = parent {
return SyntaxData(absoluteRaw, parent: parent.asSyntax)
} else {
return SyntaxData.forRoot(absoluteRaw.raw)
}
}

/// Converts this node to a `Syntax` object.
///
/// This operation results in wrapping this node and all of its parents into
/// `Syntax` objects. There's a cost associated with it that should be taken
/// into account before used inside performance critical code.
public var asSyntax: Syntax {
return Syntax(self.asSyntaxData)
}

/// The parent of this syntax node, or `nil` if this node is the root.
public var parent: SyntaxNode? {
guard !parents.isEmpty else { return nil }
return SyntaxNode(node: parents.last!, parents: parents.dropLast())
}

/// The absolute position of the starting point of this node.
public var position: AbsolutePosition {
return absoluteRaw.position
}

/// The end position of this node, including its trivia.
public var endPosition: AbsolutePosition {
return absoluteRaw.endPosition
}

/// The textual byte length of this node including leading and trailing trivia.
public var byteSize: Int {
return totalLength.utf8Length
}

/// The byte source range of this node including leading and trailing trivia.
public var byteRange: ByteSourceRange {
return ByteSourceRange(offset: position.utf8Offset, length: byteSize)
}

/// The length of this node including all of its trivia.
public var totalLength: SourceLength {
return raw.totalLength
}
}

extension SyntaxNode: Identifiable {
/// Returns a value representing the unique identity of the node.
public var id: SyntaxIdentifier {
return absoluteRaw.info.nodeId
}
}

extension SyntaxNode: CustomStringConvertible, TextOutputStreamable {
/// A source-accurate description of this node.
public var description: String {
return raw.description
}

/// Prints the raw value of this node to the provided stream.
/// - Parameter stream: The stream to which to print the raw tree.
public func write<Target>(to target: inout Target)
where Target: TextOutputStream {
raw.write(to: &target)
}
}

/// Expose `recursiveDescription` on raw nodes for debugging purposes.
extension RawSyntaxNodeProtocol {
/// Print this raw syntax node including all of its children.
Expand All @@ -766,3 +663,6 @@ extension RawSyntaxNodeProtocol {
return Syntax(raw: raw).recursiveDescription
}
}

@available(*, unavailable, message: "use 'Syntax' instead")
public struct SyntaxNode {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this useful in any way or just an artifact from your local development?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this only because SyntaxNode was a public type. Since the initializer was not public, I don't anticipate anyone is using this. But still.

Loading