Skip to content

Commit

Permalink
[SwiftSyntax] Make Syntax nodes structs instead of classes (#14122)
Browse files Browse the repository at this point in the history
* [Experiment] Make Syntax nodes structs instead of classes

* [Experiment] Add Hashable conformance to concrete types

* Fix pep8 violation

* Remove AnySyntax, explicitly specialize SyntaxCollection nodes

* Refine the comment for SyntaxCollection nodes
  • Loading branch information
harlanhaskins authored Jan 25, 2018
1 parent c665e77 commit 82177fe
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 235 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ add_swift_library(swiftSwiftSyntax SHARED
Syntax.swift
SyntaxData.swift
SyntaxChildren.swift
SyntaxCollection.swift
SyntaxCollections.swift.gyb
SyntaxBuilders.swift.gyb
SyntaxFactory.swift.gyb
SyntaxKind.swift.gyb
Expand Down
2 changes: 1 addition & 1 deletion SwiftSyntax.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ extension Syntax {
}
let decoder = JSONDecoder()
let raw = try decoder.decode(RawSyntax.self, from: result.stdoutData)
guard let file = Syntax.fromRaw(raw) as? SourceFileSyntax else {
guard let file = makeSyntax(raw) as? SourceFileSyntax else {
throw ParserError.invalidFile
}
return file
Expand Down
89 changes: 57 additions & 32 deletions Syntax.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,47 @@ import Foundation
/// A Syntax node represents a tree of nodes with tokens at the leaves.
/// Each node has accessors for its known children, and allows efficient
/// iteration over the children through its `children` property.
public class Syntax: CustomStringConvertible {
public protocol Syntax:
CustomStringConvertible, TextOutputStreamable {}

internal protocol _SyntaxBase: Syntax {
/// The type of sequence containing the indices of present children.
internal typealias PresentChildIndicesSequence =
typealias PresentChildIndicesSequence =
LazyFilterSequence<CountableRange<Int>>

/// The root of the tree this node is currently in.
internal let _root: SyntaxData
var _root: SyntaxData { get } // Must be of type SyntaxData

/// The data backing this node.
/// - note: This is unowned, because the reference to the root data keeps it
/// alive. This means there is an implicit relationship -- the data
/// property must be a descendent of the root. This relationship must
/// be preserved in all circumstances where Syntax nodes are created.
internal unowned var data: SyntaxData
var _data: SyntaxData { get }

#if DEBUG
func validate() {
// This is for subclasses to override to perform structural validation.
}
func validate()
#endif
}
extension _SyntaxBase {
public func validate() {
// This is for implementers to override to perform structural validation.
}
}

/// Creates a Syntax node from the provided root and data.
internal init(root: SyntaxData, data: SyntaxData) {
self._root = root
self.data = data
#if DEBUG
validate()
#endif
extension Syntax {
var data: SyntaxData {
guard let base = self as? _SyntaxBase else {
fatalError("only first-class syntax nodes can conform to Syntax")
}
return base._data
}

var _root: SyntaxData {
guard let base = self as? _SyntaxBase else {
fatalError("only first-class syntax nodes can conform to Syntax")
}
return base._root
}

/// Access the raw syntax assuming the node is a Syntax.
Expand Down Expand Up @@ -93,7 +106,7 @@ public class Syntax: CustomStringConvertible {
/// The parent of this syntax node, or `nil` if this node is the root.
public var parent: Syntax? {
guard let parentData = data.parent else { return nil }
return Syntax.make(root: _root, data: parentData)
return makeSyntax(root: _root, data: parentData)
}

/// The index of this node in the parent's children.
Expand All @@ -103,14 +116,14 @@ public class Syntax: CustomStringConvertible {

/// The root of the tree in which this node resides.
public var root: Syntax {
return Syntax.make(root: _root, data: _root)
return makeSyntax(root: _root, data: _root)
}

/// The sequence of indices that correspond to child nodes that are not
/// missing.
///
/// This property is an implementation detail of `SyntaxChildren`.
internal var presentChildIndices: PresentChildIndicesSequence {
internal var presentChildIndices: _SyntaxBase.PresentChildIndicesSequence {
return raw.layout.indices.lazy.filter { self.raw.layout[$0].isPresent }
}

Expand All @@ -121,7 +134,7 @@ public class Syntax: CustomStringConvertible {
public func child(at index: Int) -> Syntax? {
guard raw.layout.indices.contains(index) else { return nil }
if raw.layout[index].isMissing { return nil }
return Syntax.make(root: _root, data: data.cachedChild(at: index))
return makeSyntax(root: _root, data: data.cachedChild(at: index))
}

/// A source-accurate description of this node.
Expand All @@ -130,9 +143,7 @@ public class Syntax: CustomStringConvertible {
self.write(to: &s)
return s
}
}

extension Syntax: TextOutputStreamable {

/// 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)
Expand All @@ -141,21 +152,27 @@ extension Syntax: TextOutputStreamable {
}
}

extension Syntax: Hashable {
/// Determines if two nodes are equal to each other.
public static func ==(lhs: Syntax, rhs: Syntax) -> Bool {
return lhs.hashValue == rhs.hashValue
}
/// Use reference value as the hashValue for this Syntax
public var hashValue: Int {
return ObjectIdentifier(data).hashValue
}
/// Determines if two nodes are equal to each other.
public func ==(lhs: Syntax, rhs: Syntax) -> Bool {
return lhs.data === rhs.data
}

/// MARK: - Nodes

/// A Syntax node representing a single token.
public class TokenSyntax: Syntax {
public struct TokenSyntax: _SyntaxBase, Hashable {
var _root: SyntaxData
unowned var _data: SyntaxData

/// Creates a Syntax node from the provided root and data.
internal init(root: SyntaxData, data: SyntaxData) {
self._root = root
self._data = data
#if DEBUG
validate()
#endif
}

/// The text of the token as written in the source code.
public var text: String {
return tokenKind.text
Expand Down Expand Up @@ -230,4 +247,12 @@ public class TokenSyntax: Syntax {
}
return kind
}

public static func ==(lhs: TokenSyntax, rhs: TokenSyntax) -> Bool {
return lhs._data === rhs._data
}

public var hashValue: Int {
return ObjectIdentifier(_data).hashValue
}
}
2 changes: 1 addition & 1 deletion SyntaxBuilders.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ extension ${node.name} {
/// incrementally build the structure of the node.
/// - Returns: A `${node.name}` with all the fields populated in the builder
/// closure.
public convenience init(_ build: (inout ${Builder}) -> Void) {
public init(_ build: (inout ${Builder}) -> Void) {
var builder = ${Builder}()
build(&builder)
let data = builder.buildData()
Expand Down
2 changes: 1 addition & 1 deletion SyntaxChildren.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Foundation
public struct SyntaxChildren: Sequence {
public struct Iterator: IteratorProtocol {
let node: Syntax
var indexIterator: Syntax.PresentChildIndicesSequence.Iterator
var indexIterator: _SyntaxBase.PresentChildIndicesSequence.Iterator

init(node: Syntax) {
self.indexIterator = node.presentChildIndices.makeIterator()
Expand Down
150 changes: 0 additions & 150 deletions SyntaxCollection.swift

This file was deleted.

Loading

0 comments on commit 82177fe

Please sign in to comment.