blob: 8f4535f6f04fa1757a5633ead382c7b87a0c96a5 [file] [log] [blame]
//===-------------------- Syntax.swift - Syntax Protocol ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
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 protocol Syntax:
CustomStringConvertible, TextOutputStreamable {}
internal protocol _SyntaxBase: Syntax {
/// The type of sequence containing the indices of present children.
typealias PresentChildIndicesSequence =
LazyFilterSequence<Range<Int>>
/// The root of the tree this node is currently in.
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.
var _data: SyntaxData { get }
#if DEBUG
func validate()
#endif
}
extension _SyntaxBase {
public func validate() {
// This is for implementers to override to perform structural validation.
}
}
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.
var raw: RawSyntax {
return data.raw
}
/// An iterator over children of this node.
public var children: SyntaxChildren {
return SyntaxChildren(node: self)
}
/// The number of children, `present` or `missing`, in this node.
/// This value can be used safely with `child(at:)`.
public var numberOfChildren: Int {
return data.childCaches.count
}
/// Whether or not this node is marked as `present`.
public var isPresent: Bool {
return raw.isPresent
}
/// Whether or not this node is marked as `missing`.
public var isMissing: Bool {
return raw.isMissing
}
/// Whether or not this node represents an Expression.
public var isExpr: Bool {
return raw.kind.isExpr
}
/// Whether or not this node represents a Declaration.
public var isDecl: Bool {
return raw.kind.isDecl
}
/// Whether or not this node represents a Statement.
public var isStmt: Bool {
return raw.kind.isStmt
}
/// Whether or not this node represents a Type.
public var isType: Bool {
return raw.kind.isType
}
/// Whether or not this node represents a Pattern.
public var isPattern: Bool {
return raw.kind.isPattern
}
/// 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 makeSyntax(root: _root, data: parentData)
}
/// The index of this node in the parent's children.
public var indexInParent: Int {
return data.indexInParent
}
/// The absolute position of the starting point of this node. If the first token
/// is with leading trivia, the position points to the start of the leading
/// trivia.
public var position: AbsolutePosition {
return data.position
}
/// The absolute position of the starting point of this node, skipping any
/// leading trivia attached to the first token syntax.
public var positionAfterSkippingLeadingTrivia: AbsolutePosition {
return data.positionAfterSkippingLeadingTrivia
}
/// The textual byte length of this node including leading and trailing trivia.
public var byteSize: Int {
return data.byteSize
}
/// The leading trivia of this syntax node. Leading trivia is attached to
/// the first token syntax contained by this node. Without such token, this
/// property will return nil.
public var leadingTrivia: Trivia? {
return raw.leadingTrivia
}
/// The trailing trivia of this syntax node. Trailing trivia is attached to
/// the last token syntax contained by this node. Without such token, this
/// property will return nil.
public var trailingTrivia: Trivia? {
return raw.trailingTrivia
}
/// When isImplicit is true, the syntax node doesn't include any
/// underlying tokens, e.g. an empty CodeBlockItemList.
public var isImplicit: Bool {
return leadingTrivia == nil
}
/// The textual byte length of this node exluding leading and trailing trivia.
public var byteSizeAfterTrimmingTrivia: Int {
return data.byteSize - (leadingTrivia?.byteSize ?? 0) -
(trailingTrivia?.byteSize ?? 0)
}
/// The root of the tree in which this node resides.
public var root: Syntax {
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: _SyntaxBase.PresentChildIndicesSequence {
return raw.layout.indices.lazy.filter {
self.raw.layout[$0]?.isPresent == true
}
}
/// Gets the child at the provided index in this node's children.
/// - Parameter index: The index of the child node you're looking for.
/// - Returns: A Syntax node for the provided child, or `nil` if there
/// is not a child at that index in the node.
public func child(at index: Int) -> Syntax? {
guard raw.layout.indices.contains(index) else { return nil }
guard let childData = data.cachedChild(at: index) else { return nil }
return makeSyntax(root: _root, data: childData)
}
/// A source-accurate description of this node.
public var description: String {
var s = ""
self.write(to: &s)
return s
}
/// 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 {
data.raw.write(to: &target)
}
}
/// 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 struct TokenSyntax: _SyntaxBase, Hashable {
let _root: SyntaxData
unowned let _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
}
/// Returns a new TokenSyntax with its kind replaced
/// by the provided token kind.
public func withKind(_ tokenKind: TokenKind) -> TokenSyntax {
guard case let .token(_, leadingTrivia, trailingTrivia, presence) = raw else {
fatalError("TokenSyntax must have token as its raw")
}
let (root, newData) = data.replacingSelf(.token(tokenKind, leadingTrivia,
trailingTrivia, presence))
return TokenSyntax(root: root, data: newData)
}
/// Returns a new TokenSyntax with its leading trivia replaced
/// by the provided trivia.
public func withLeadingTrivia(_ leadingTrivia: Trivia) -> TokenSyntax {
guard case let .token(kind, _, trailingTrivia, presence) = raw else {
fatalError("TokenSyntax must have token as its raw")
}
let (root, newData) = data.replacingSelf(.token(kind, leadingTrivia,
trailingTrivia, presence))
return TokenSyntax(root: root, data: newData)
}
/// Returns a new TokenSyntax with its trailing trivia replaced
/// by the provided trivia.
public func withTrailingTrivia(_ trailingTrivia: Trivia) -> TokenSyntax {
guard case let .token(kind, leadingTrivia, _, presence) = raw else {
fatalError("TokenSyntax must have token as its raw")
}
let (root, newData) = data.replacingSelf(.token(kind, leadingTrivia,
trailingTrivia, presence))
return TokenSyntax(root: root, data: newData)
}
/// Returns a new TokenSyntax with its leading trivia removed.
public func withoutLeadingTrivia() -> TokenSyntax {
return withLeadingTrivia([])
}
/// Returns a new TokenSyntax with its trailing trivia removed.
public func withoutTrailingTrivia() -> TokenSyntax {
return withTrailingTrivia([])
}
/// Returns a new TokenSyntax with all trivia removed.
public func withoutTrivia() -> TokenSyntax {
return withoutLeadingTrivia().withoutTrailingTrivia()
}
/// The leading trivia (spaces, newlines, etc.) associated with this token.
public var leadingTrivia: Trivia {
guard case .token(_, let leadingTrivia, _, _) = raw else {
fatalError("TokenSyntax must have token as its raw")
}
return leadingTrivia
}
/// The trailing trivia (spaces, newlines, etc.) associated with this token.
public var trailingTrivia: Trivia {
guard case .token(_, _, let trailingTrivia, _) = raw else {
fatalError("TokenSyntax must have token as its raw")
}
return trailingTrivia
}
/// The kind of token this node represents.
public var tokenKind: TokenKind {
guard case .token(let kind, _, _, _) = raw else {
fatalError("TokenSyntax must have token as its raw")
}
return kind
}
public static func ==(lhs: TokenSyntax, rhs: TokenSyntax) -> Bool {
return lhs._data === rhs._data
}
public var hashValue: Int {
return ObjectIdentifier(_data).hashValue
}
}