| //===-------------------- 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 class Syntax: CustomStringConvertible { |
| /// The type of sequence containing the indices of present children. |
| internal typealias PresentChildIndicesSequence = |
| LazyFilterSequence<CountableRange<Int>> |
| |
| /// The root of the tree this node is currently in. |
| internal let _root: 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 |
| |
| #if DEBUG |
| func validate() { |
| // This is for subclasses to override to perform structural validation. |
| } |
| #endif |
| |
| /// 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 |
| } |
| |
| /// 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) |
| } |
| |
| /// Whether or not this node it marked as `present`. |
| public var isPresent: Bool { |
| return raw.presence == .present |
| } |
| |
| /// Whether or not this node it marked as `missing`. |
| public var isMissing: Bool { |
| return raw.presence == .missing |
| } |
| |
| /// 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 Syntax.make(root: _root, data: parentData) |
| } |
| |
| /// The index of this node in the parent's children. |
| public var indexInParent: Int { |
| return data.indexInParent |
| } |
| |
| /// The root of the tree in which this node resides. |
| public var root: Syntax { |
| return Syntax.make(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 { |
| return raw.layout.indices.lazy.filter { self.raw.layout[$0].isPresent } |
| } |
| |
| /// 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 } |
| if raw.layout[index].isMissing { return nil } |
| return Syntax.make(root: _root, data: data.cachedChild(at: index)) |
| } |
| |
| /// A source-accurate description of this node. |
| public var description: String { |
| var s = "" |
| 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) |
| where Target: TextOutputStream { |
| data.raw.write(to: &target) |
| } |
| } |
| |
| extension Syntax: Equatable { |
| /// Determines if two nodes are equal to each other. |
| public static func ==(lhs: Syntax, rhs: Syntax) -> Bool { |
| return lhs.data === rhs.data |
| } |
| } |
| |
| /// MARK: - Nodes |
| |
| /// A Syntax node representing a single token. |
| public class TokenSyntax: Syntax { |
| /// The text of the token as written in the source code. |
| public var text: String { |
| return tokenKind.text |
| } |
| |
| 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 |
| } |
| } |