| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| //===------- SwiftcInvocation.swift - Utilities for invoking swiftc -------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // This file provides the logic for invoking swiftc to parse Swift files. | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| import Foundation | ||
|
|
||
| #if os(macOS) | ||
| import Darwin | ||
| #elseif os(Linux) | ||
| import Glibc | ||
| #endif | ||
|
|
||
| /// The result of process execution, containing the exit status code, | ||
| /// stdout, and stderr | ||
| struct ProcessResult { | ||
| /// The process exit code. A non-zero exit code usually indicates failure. | ||
| let exitCode: Int | ||
|
|
||
| /// The contents of the process's stdout as Data. | ||
| let stdoutData: Data | ||
|
|
||
| /// The contents of the process's stderr as Data. | ||
| let stderrData: Data | ||
|
|
||
| /// The contents of the process's stdout, assuming the data was UTF-8 encoded. | ||
| var stdout: String { | ||
| return String(data: stdoutData, encoding: .utf8)! | ||
| } | ||
|
|
||
| /// The contents of the process's stderr, assuming the data was UTF-8 encoded. | ||
| var stderr: String { | ||
| return String(data: stderrData, encoding: .utf8)! | ||
| } | ||
|
|
||
| /// Whether or not this process had a non-zero exit code. | ||
| var wasSuccessful: Bool { | ||
| return exitCode == 0 | ||
| } | ||
| } | ||
|
|
||
| /// Runs the provided executable with the provided arguments and returns the | ||
| /// contents of stdout and stderr as Data. | ||
| /// - Parameters: | ||
| /// - executable: The full file URL to the executable you're running. | ||
| /// - arguments: A list of strings to pass to the process as arguments. | ||
| /// - Returns: A ProcessResult containing stdout, stderr, and the exit code. | ||
| func run(_ executable: URL, arguments: [String] = []) -> ProcessResult { | ||
| let stdoutPipe = Pipe() | ||
| var stdoutData = Data() | ||
| stdoutPipe.fileHandleForReading.readabilityHandler = { file in | ||
| stdoutData.append(file.availableData) | ||
| } | ||
|
|
||
| let stderrPipe = Pipe() | ||
| var stderrData = Data() | ||
| stderrPipe.fileHandleForReading.readabilityHandler = { file in | ||
| stderrData.append(file.availableData) | ||
| } | ||
|
|
||
| let process = Process() | ||
|
|
||
| process.terminationHandler = { process in | ||
| stdoutPipe.fileHandleForReading.readabilityHandler = nil | ||
| stderrPipe.fileHandleForReading.readabilityHandler = nil | ||
| } | ||
|
|
||
| process.launchPath = executable.path | ||
| process.arguments = arguments | ||
| process.standardOutput = stdoutPipe | ||
| process.standardError = stderrPipe | ||
| process.launch() | ||
| process.waitUntilExit() | ||
| return ProcessResult(exitCode: Int(process.terminationStatus), | ||
| stdoutData: stdoutData, | ||
| stderrData: stderrData) | ||
| } | ||
|
|
||
| /// Finds the dylib or executable which the provided address falls in. | ||
| /// - Parameter dsohandle: A pointer to a symbol in the object file you're | ||
| /// looking for. If not provided, defaults to the | ||
| /// caller's `#dsohandle`, which will give you the | ||
| /// object file the caller resides in. | ||
| /// - Returns: A File URL pointing to the object where the provided address | ||
| /// resides. This may be a dylib, shared object, static library, | ||
| /// or executable. If unable to find the appropriate object, returns | ||
| /// `nil`. | ||
| func findFirstObjectFile(for dsohandle: UnsafeRawPointer = #dsohandle) -> URL? { | ||
| var info = dl_info() | ||
| if dladdr(dsohandle, &info) == 0 { | ||
| return nil | ||
| } | ||
| let path = String(cString: info.dli_fname) | ||
| return URL(fileURLWithPath: path) | ||
| } | ||
|
|
||
| enum InvocationError: Error { | ||
| case couldNotFindSwiftc | ||
| case couldNotFindSDK | ||
| case abort(code: Int) | ||
| } | ||
|
|
||
| struct SwiftcRunner { | ||
| /// Gets the `swiftc` binary packaged alongside this library. | ||
| /// - Returns: The path to `swiftc` relative to the path of this library | ||
| /// file, or `nil` if it could not be found. | ||
| /// - Note: This makes assumptions about your Swift installation directory | ||
| /// structure. Importantly, it assumes that the directory tree is | ||
| /// shaped like this: | ||
| /// ``` | ||
| /// install_root/ | ||
| /// - bin/ | ||
| /// - swiftc | ||
| /// - lib/ | ||
| /// - swift/ | ||
| /// - ${target}/ | ||
| /// - libswiftSwiftSyntax.[dylib|so] | ||
| /// ``` | ||
| static func locateSwiftc() -> URL? { | ||
| guard let libraryPath = findFirstObjectFile() else { return nil } | ||
| let swiftcURL = libraryPath.deletingLastPathComponent() | ||
| .deletingLastPathComponent() | ||
| .deletingLastPathComponent() | ||
| .deletingLastPathComponent() | ||
| .appendingPathComponent("bin") | ||
| .appendingPathComponent("swiftc") | ||
| guard FileManager.default.fileExists(atPath: swiftcURL.path) else { | ||
| return nil | ||
| } | ||
| return swiftcURL | ||
| } | ||
|
|
||
| #if os(macOS) | ||
| /// The location of the macOS SDK, or `nil` if it could not be found. | ||
| static let macOSSDK: String? = { | ||
| let url = URL(fileURLWithPath: "/usr/bin/env") | ||
| let result = run(url, arguments: ["xcrun", "--show-sdk-path"]) | ||
| guard result.wasSuccessful else { return nil } | ||
| let toolPath = result.stdout.trimmingCharacters(in: .whitespacesAndNewlines) | ||
| if toolPath.isEmpty { return nil } | ||
| return toolPath | ||
| }() | ||
| #endif | ||
|
|
||
| /// Internal static cache of the Swiftc path. | ||
| static let _swiftcURL: URL? = SwiftcRunner.locateSwiftc() | ||
|
|
||
| /// The URL where the `swiftc` binary lies. | ||
| let swiftcURL: URL | ||
|
|
||
| /// The source file being parsed. | ||
| let sourceFile: URL | ||
|
|
||
| /// Creates a SwiftcRunner that will parse and emit the syntax | ||
| /// tree for a provided source file. | ||
| /// - Parameter sourceFile: The URL to the source file you're trying | ||
| /// to parse. | ||
| init(sourceFile: URL) throws { | ||
| guard let url = SwiftcRunner._swiftcURL else { | ||
| throw InvocationError.couldNotFindSwiftc | ||
| } | ||
| self.swiftcURL = url | ||
| self.sourceFile = sourceFile | ||
| } | ||
|
|
||
| /// Invokes swiftc with the provided arguments. | ||
| func invoke() throws -> ProcessResult { | ||
| var arguments = ["-frontend", "-emit-syntax"] | ||
| arguments.append(sourceFile.path) | ||
| #if os(macOS) | ||
| guard let sdk = SwiftcRunner.macOSSDK else { | ||
| throw InvocationError.couldNotFindSDK | ||
| } | ||
| arguments.append("-sdk") | ||
| arguments.append(sdk) | ||
| #endif | ||
| return run(swiftcURL, arguments: arguments) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| //===-------------------- 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 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) | ||
| } | ||
|
|
||
| /// 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 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
|
|
||
| %{ | ||
| # -*- mode: Swift -*- | ||
| from gyb_syntax_support import * | ||
| NODE_MAP = create_node_map() | ||
| # Ignore the following admonition; it applies to the resulting .swift file only | ||
| }% | ||
| //// Automatically Generated From SyntaxBuilders.swift.gyb. | ||
| //// Do Not Edit Directly! | ||
| //===------------ SyntaxBuilders.swift - Syntax Builder definitions -------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| %{ | ||
| """ | ||
| A builder is a struct with a mutable layout inside. Clients can | ||
| specify as many or as few of the children as they want, and the structure is | ||
| guaranted to be structurally (if not syntactically) valid. | ||
| """ | ||
| }% | ||
|
|
||
| % for node in SYNTAX_NODES: | ||
| % if node.is_buildable(): | ||
| % Builder = node.name + "Builder" | ||
| public struct ${Builder} { | ||
| private var layout = [ | ||
| % for child in node.children: | ||
| ${make_missing_swift_child(child)}, | ||
| % end | ||
| ] | ||
| internal init() {} | ||
| % for child in node.children: | ||
| % child_node = NODE_MAP.get(child.syntax_kind) | ||
| % if child_node and child_node.is_syntax_collection(): | ||
| % child_elt = child_node.collection_element_name | ||
| % child_elt_type = child_node.collection_element_type | ||
|
|
||
| public mutating func add${child_elt}(_ elt: ${child_elt_type}) { | ||
| let idx = ${node.name}.Cursor.${child.swift_name}.rawValue | ||
| layout[idx] = layout[idx].appending(elt.raw) | ||
| } | ||
| % else: | ||
|
|
||
| public mutating func use${child.name}(_ node: ${child.type_name}) { | ||
| let idx = ${node.name}.Cursor.${child.swift_name}.rawValue | ||
| layout[idx] = node.raw | ||
| } | ||
| % end | ||
| % end | ||
|
|
||
| internal func buildData() -> SyntaxData { | ||
| return SyntaxData(raw: .node(.${node.swift_syntax_kind}, | ||
| layout, .present)) | ||
| } | ||
| } | ||
|
|
||
| extension ${node.name} { | ||
| /// Creates a `${node.name}` using the provided build function. | ||
| /// - Parameter: | ||
| /// - build: A closure that wil be invoked in order to initialize | ||
| /// the fields of the syntax node. | ||
| /// This closure is passed a `${Builder}` which you can use to | ||
| /// 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) { | ||
| var builder = ${Builder}() | ||
| build(&builder) | ||
| let data = builder.buildData() | ||
| self.init(root: data, data: data) | ||
| } | ||
| } | ||
|
|
||
| % end | ||
| % end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| //===------------- SyntaxChildren.swift - Syntax Child Iterator -----------===// | ||
| // | ||
| // 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 | ||
|
|
||
| public struct SyntaxChildren: Sequence { | ||
| public struct Iterator: IteratorProtocol { | ||
| var index: Int = 0 | ||
| let node: Syntax | ||
|
|
||
| public mutating func next() -> Syntax? { | ||
| defer { index += 1 } | ||
| return node.child(at: index) | ||
| } | ||
| } | ||
| let node: Syntax | ||
|
|
||
| public func makeIterator() -> SyntaxChildren.Iterator { | ||
| return Iterator(index: 0, node: node) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| //===-------------- SyntaxCollection.swift - Syntax Collection ------------===// | ||
| // | ||
| // 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 | ||
|
|
||
| /// Represents a collection of Syntax nodes of a specific type. SyntaxCollection | ||
| /// behaves as a regular Swift collection, and has accessors that return new | ||
| /// versions of the collection with different children. | ||
| public class SyntaxCollection<SyntaxElement: Syntax>: Syntax { | ||
| /// Creates a new SyntaxCollection by replacing the underlying layout with | ||
| /// a different set of raw syntax nodes. | ||
| /// | ||
| /// - Parameter layout: The new list of raw syntax nodes underlying this | ||
| /// collection. | ||
| /// - Returns: A new SyntaxCollection with the new layout underlying it. | ||
| internal func replacingLayout( | ||
| _ layout: [RawSyntax]) -> SyntaxCollection<SyntaxElement> { | ||
| let newRaw = data.raw.replacingLayout(layout) | ||
| let (newRoot, newData) = data.replacingSelf(newRaw) | ||
| return SyntaxCollection<SyntaxElement>(root: newRoot, data: newData) | ||
| } | ||
|
|
||
| /// Creates a new SyntaxCollection by appending the provided syntax element | ||
| /// to the children. | ||
| /// | ||
| /// - Parameter syntax: The element to append. | ||
| /// - Returns: A new SyntaxCollection with that element appended to the end. | ||
| public func appending( | ||
| _ syntax: SyntaxElement) -> SyntaxCollection<SyntaxElement> { | ||
| var newLayout = data.raw.layout | ||
| newLayout.append(syntax.raw) | ||
| return replacingLayout(newLayout) | ||
| } | ||
|
|
||
| /// Creates a new SyntaxCollection by prepending the provided syntax element | ||
| /// to the children. | ||
| /// | ||
| /// - Parameter syntax: The element to prepend. | ||
| /// - Returns: A new SyntaxCollection with that element prepended to the | ||
| /// beginning. | ||
| public func prepending( | ||
| _ syntax: SyntaxElement) -> SyntaxCollection<SyntaxElement> { | ||
| return inserting(syntax, at: 0) | ||
| } | ||
|
|
||
| /// Creates a new SyntaxCollection by inserting the provided syntax element | ||
| /// at the provided index in the children. | ||
| /// | ||
| /// - Parameters: | ||
| /// - syntax: The element to insert. | ||
| /// - index: The index at which to insert the element in the collection. | ||
| /// | ||
| /// - Returns: A new SyntaxCollection with that element appended to the end. | ||
| public func inserting(_ syntax: SyntaxElement, | ||
| at index: Int) -> SyntaxCollection<SyntaxElement> { | ||
| var newLayout = data.raw.layout | ||
| /// Make sure the index is a valid insertion index (0 to 1 past the end) | ||
| precondition((newLayout.startIndex...newLayout.endIndex).contains(index), | ||
| "inserting node at invalid index \(index)") | ||
| newLayout.insert(syntax.raw, at: index) | ||
| return replacingLayout(newLayout) | ||
| } | ||
|
|
||
| /// Creates a new SyntaxCollection by removing the syntax element at the | ||
| /// provided index. | ||
| /// | ||
| /// - Parameter index: The index of the element to remove from the collection. | ||
| /// - Returns: A new SyntaxCollection with the element at the provided index | ||
| /// removed. | ||
| public func removing(childAt index: Int) -> SyntaxCollection<SyntaxElement> { | ||
| var newLayout = data.raw.layout | ||
| newLayout.remove(at: index) | ||
| return replacingLayout(newLayout) | ||
| } | ||
|
|
||
| /// Creates a new SyntaxCollection by removing the first element. | ||
| /// | ||
| /// - Returns: A new SyntaxCollection with the first element removed. | ||
| public func removingFirst() -> SyntaxCollection<SyntaxElement> { | ||
| var newLayout = data.raw.layout | ||
| newLayout.removeFirst() | ||
| return replacingLayout(newLayout) | ||
| } | ||
|
|
||
| /// Creates a new SyntaxCollection by removing the last element. | ||
| /// | ||
| /// - Returns: A new SyntaxCollection with the last element removed. | ||
| public func removingLast() -> SyntaxCollection<SyntaxElement> { | ||
| var newLayout = data.raw.layout | ||
| newLayout.removeLast() | ||
| return replacingLayout(newLayout) | ||
| } | ||
| } | ||
|
|
||
| /// Conformance for SyntaxCollection to the Collection protocol. | ||
| extension SyntaxCollection: Collection { | ||
| public var startIndex: Int { | ||
| return data.childCaches.startIndex | ||
| } | ||
|
|
||
| public var endIndex: Int { | ||
| return data.childCaches.endIndex | ||
| } | ||
|
|
||
| public func index(after i: Int) -> Int { | ||
| return data.childCaches.index(after: i) | ||
| } | ||
|
|
||
| public subscript(_ index: Int) -> SyntaxElement { | ||
| return child(at: index)! as! SyntaxElement | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| //===-------------------- SyntaxData.swift - Syntax Data ------------------===// | ||
| // | ||
| // 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 unique identifier for a node in the tree. | ||
| /// Currently, this is an index path from the current node to the root of the | ||
| /// tree. It's an implementation detail and shouldn't be | ||
| /// exposed to clients. | ||
| typealias NodeIdentifier = [Int] | ||
|
|
||
| /// SyntaxData is the underlying storage for each Syntax node. | ||
| /// It's modelled as an array that stores and caches a SyntaxData for each raw | ||
| /// syntax node in its layout. It is up to the specific Syntax nodes to maintain | ||
| /// the correct layout of their SyntaxData backing stores. | ||
| /// | ||
| /// SyntaxData is an implementation detail, and should not be exposed to clients | ||
| /// of libSyntax. | ||
| /// | ||
| /// The following relationships are preserved: | ||
| /// parent.cachedChild(at: indexInParent) === self | ||
| /// raw.layout.count == childCaches.count | ||
| /// pathToRoot.first === indexInParent | ||
| final class SyntaxData: Equatable { | ||
|
|
||
| let raw: RawSyntax | ||
| let indexInParent: Int | ||
| weak var parent: SyntaxData? | ||
|
|
||
| let childCaches: [AtomicCache<SyntaxData>] | ||
|
|
||
| /// Creates a SyntaxData with the provided raw syntax, pointing to the | ||
| /// provided index, in the provided parent. | ||
| /// - Parameters: | ||
| /// - raw: The raw syntax underlying this node. | ||
| /// - indexInParent: The index in the parent's layout where this node will | ||
| /// reside. | ||
| /// - parent: The parent of this node, or `nil` if this node is the root. | ||
| required init(raw: RawSyntax, indexInParent: Int = 0, | ||
| parent: SyntaxData? = nil) { | ||
| self.raw = raw | ||
| self.indexInParent = indexInParent | ||
| self.parent = parent | ||
| self.childCaches = raw.layout.map { _ in AtomicCache<SyntaxData>() } | ||
| } | ||
|
|
||
| /// The index path from this node to the root. This can be used to uniquely | ||
| /// identify this node in the tree. | ||
| lazy private(set) var pathToRoot: NodeIdentifier = { | ||
| var path = [Int]() | ||
| var node = self | ||
| while let parent = node.parent { | ||
| path.append(node.indexInParent) | ||
| node = parent | ||
| } | ||
| return path | ||
| }() | ||
|
|
||
| /// Returns the child data at the provided index in this data's layout. | ||
| /// This child is cached and will be used in subsequent accesses. | ||
| /// - Note: This function traps if the index is out of the bounds of the | ||
| /// data's layout. | ||
| /// | ||
| /// - Parameter index: The index to create and cache. | ||
| /// - Returns: The child's data at the provided index. | ||
| func cachedChild(at index: Int) -> SyntaxData { | ||
| return childCaches[index].value { realizeChild(index) } | ||
| } | ||
|
|
||
| /// Returns the child data at the provided cursor in this data's layout. | ||
| /// This child is cached and will be used in subsequent accesses. | ||
| /// - Note: This function traps if the cursor is out of the bounds of the | ||
| /// data's layout. | ||
| /// | ||
| /// - Parameter cursor: The cursor to create and cache. | ||
| /// - Returns: The child's data at the provided cursor. | ||
| func cachedChild<CursorType: RawRepresentable>( | ||
| at cursor: CursorType) -> SyntaxData | ||
| where CursorType.RawValue == Int { | ||
| return cachedChild(at: cursor.rawValue) | ||
| } | ||
|
|
||
| /// Walks up the provided node's parent tree looking for the receiver. | ||
| /// - parameter data: The potential child data. | ||
| /// - returns: `true` if the receiver exists in the parent chain of the | ||
| /// provided data. | ||
| /// - seealso: isDescendent(of:) | ||
| func isAncestor(of data: SyntaxData) -> Bool { | ||
| return data.isDescendent(of: self) | ||
| } | ||
|
|
||
| /// Walks up the receiver's parent tree looking for the provided node. | ||
| /// - parameter data: The potential ancestor data. | ||
| /// - returns: `true` if the data exists in the parent chain of the receiver. | ||
| /// - seealso: isAncestor(of:) | ||
| func isDescendent(of data: SyntaxData) -> Bool { | ||
| if data == self { return true } | ||
| var node = self | ||
| while let parent = node.parent { | ||
| if parent == data { return true } | ||
| node = parent | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| /// Creates a copy of `self` and recursively creates `SyntaxData` nodes up to | ||
| /// the root. | ||
| /// - parameter newRaw: The new RawSyntax that will back the new `Data` | ||
| /// - returns: A tuple of both the new root node and the new data with the raw | ||
| /// layout replaced. | ||
| func replacingSelf( | ||
| _ newRaw: RawSyntax) -> (root: SyntaxData, newValue: SyntaxData) { | ||
| // If we have a parent already, then ask our current parent to copy itself | ||
| // recursively up to the root. | ||
| if let parent = parent { | ||
| let (root, newParent) = parent.replacingChild(newRaw, at: indexInParent) | ||
| let newMe = newParent.cachedChild(at: indexInParent) | ||
| return (root: root, newValue: newMe) | ||
| } else { | ||
| // Otherwise, we're already the root, so return the new data as both the | ||
| // new root and the new data. | ||
| let newMe = SyntaxData(raw: newRaw, indexInParent: indexInParent, | ||
| parent: nil) | ||
| return (root: newMe, newValue: newMe) | ||
| } | ||
| } | ||
|
|
||
| /// Creates a copy of `self` with the child at the provided index replaced | ||
| /// with a new SyntaxData containing the raw syntax provided. | ||
| /// | ||
| /// - Parameters: | ||
| /// - child: The raw syntax for the new child to replace. | ||
| /// - index: The index pointing to where in the raw layout to place this | ||
| /// child. | ||
| /// - Returns: The new root node created by this operation, and the new child | ||
| /// syntax data. | ||
| /// - SeeAlso: replacingSelf(_:) | ||
| func replacingChild(_ child: RawSyntax, | ||
| at index: Int) -> (root: SyntaxData, newValue: SyntaxData) { | ||
| let newRaw = raw.replacingChild(index, with: child) | ||
| return replacingSelf(newRaw) | ||
| } | ||
|
|
||
| /// Creates a copy of `self` with the child at the provided cursor replaced | ||
| /// with a new SyntaxData containing the raw syntax provided. | ||
| /// | ||
| /// - Parameters: | ||
| /// - child: The raw syntax for the new child to replace. | ||
| /// - cursor: A cursor that points to the index of the child you wish to | ||
| /// replace | ||
| /// - Returns: The new root node created by this operation, and the new child | ||
| /// syntax data. | ||
| /// - SeeAlso: replacingSelf(_:) | ||
| func replacingChild<CursorType: RawRepresentable>(_ child: RawSyntax, | ||
| at cursor: CursorType) -> (root: SyntaxData, newValue: SyntaxData) | ||
| where CursorType.RawValue == Int { | ||
| return replacingChild(child, at: cursor.rawValue) | ||
| } | ||
|
|
||
| /// Creates the child's syntax data for the provided cursor. | ||
| /// | ||
| /// - Parameter cursor: The cursor pointing into the raw syntax's layout for | ||
| /// the child you're creating. | ||
| /// - Returns: A new SyntaxData for the specific child you're | ||
| /// creating, whose parent is pointing to self. | ||
| func realizeChild<CursorType: RawRepresentable>( | ||
| _ cursor: CursorType) -> SyntaxData | ||
| where CursorType.RawValue == Int { | ||
| return realizeChild(cursor.rawValue) | ||
| } | ||
|
|
||
| /// Creates the child's syntax data for the provided index. | ||
| /// | ||
| /// - Parameter cursor: The cursor pointing into the raw syntax's layout for | ||
| /// the child you're creating. | ||
| /// - Returns: A new SyntaxData for the specific child you're | ||
| /// creating, whose parent is pointing to self. | ||
| func realizeChild(_ index: Int) -> SyntaxData { | ||
| return SyntaxData(raw: raw.layout[index], | ||
| indexInParent: index, | ||
| parent: self) | ||
| } | ||
|
|
||
| /// Tells whether two SyntaxData nodes have the same identity. | ||
| /// This is not structural equality. | ||
| /// - Returns: True if both datas are exactly the same. | ||
| static func ==(lhs: SyntaxData, rhs: SyntaxData) -> Bool { | ||
| return lhs === rhs | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,205 @@ | ||
| %{ | ||
| from gyb_syntax_support import * | ||
| # -*- mode: Swift -*- | ||
| # Ignore the following admonition it applies to the resulting .swift file only | ||
| }% | ||
| //// Automatically Generated From SyntaxFactory.swift.gyb. | ||
| //// Do Not Edit Directly! | ||
| //===------- SyntaxFactory.swift - Syntax Factory implementations ---------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file defines the SyntaxFactory, one of the most important client-facing | ||
| // types in lib/Syntax and likely to be very commonly used. | ||
| // | ||
| // Effectively a namespace, SyntaxFactory is never instantiated, but is *the* | ||
| // one-stop shop for making new Syntax nodes. Putting all of these into a | ||
| // collection of static methods provides a single point of API lookup for | ||
| // clients' convenience. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| public enum SyntaxFactory { | ||
| public static func makeToken(_ kind: TokenKind, presence: SourcePresence, | ||
| leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> TokenSyntax { | ||
| let data = SyntaxData(raw: .token(kind, leadingTrivia, | ||
| trailingTrivia, presence)) | ||
| return TokenSyntax(root: data, data: data) | ||
| } | ||
|
|
||
| public static func makeUnknownSyntax(tokens: [TokenSyntax]) -> Syntax { | ||
| let data = SyntaxData(raw: .node(.unknown, | ||
| tokens.map { $0.data.raw }, | ||
| .present)) | ||
| return Syntax(root: data, data: data) | ||
| } | ||
|
|
||
| /// MARK: Syntax Node Creation APIs | ||
|
|
||
| % for node in SYNTAX_NODES: | ||
| % if node.children: | ||
| % child_params = [] | ||
| % for child in node.children: | ||
| % param_type = child.type_name | ||
| % if child.is_optional: | ||
| % param_type = param_type + "?" | ||
| % child_params.append("%s: %s" % (child.swift_name, param_type)) | ||
| % child_params = ', '.join(child_params) | ||
| public static func make${node.syntax_kind}(${child_params}) -> ${node.name} { | ||
| let data = SyntaxData(raw: .node(.${node.swift_syntax_kind}, [ | ||
| % for child in node.children: | ||
| % if child.is_optional: | ||
| ${child.swift_name}?.data.raw ?? ${make_missing_swift_child(child)}, | ||
| % else: | ||
| ${child.swift_name}.data.raw, | ||
| % end | ||
| % end | ||
| ], .present)) | ||
| return ${node.name}(root: data, data: data) | ||
| } | ||
| % elif node.is_syntax_collection(): | ||
| public static func make${node.syntax_kind}( | ||
| _ elements: [${node.collection_element_type}]) -> ${node.name} { | ||
| let data = SyntaxData(raw: .node(.${node.swift_syntax_kind}, | ||
| elements.map { $0.data.raw }, .present)) | ||
| return ${node.name}(root: data, data: data) | ||
| } | ||
| % end | ||
|
|
||
| public static func makeBlank${node.syntax_kind}() -> ${node.name} { | ||
| let data = SyntaxData(raw: .node(.${node.swift_syntax_kind}, [ | ||
| % for child in node.children: | ||
| ${make_missing_swift_child(child)}, | ||
| % end | ||
| ], .present)) | ||
| return ${node.name}(root: data, data: data) | ||
| } | ||
| % end | ||
|
|
||
| /// MARK: Token Creation APIs | ||
|
|
||
| % for token in SYNTAX_TOKENS: | ||
| % if token.is_keyword: | ||
| public static func make${token.name}Keyword(leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> TokenSyntax { | ||
| return makeToken(.${token.swift_kind()}, presence: .present, | ||
| leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| } | ||
| % elif token.text: | ||
| public static func make${token.name}Token(leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> TokenSyntax { | ||
| return makeToken(.${token.swift_kind()}, presence: .present, | ||
| leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| } | ||
| % else: | ||
| public static func make${token.name}(_ text: String, | ||
| leadingTrivia: Trivia = [], trailingTrivia: Trivia = []) -> TokenSyntax { | ||
| return makeToken(.${token.swift_kind()}(text), presence: .present, | ||
| leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| } | ||
| % end | ||
| % end | ||
|
|
||
| /// MARK: Convenience APIs | ||
|
|
||
| public static func makeVoidTupleType() -> TupleTypeSyntax { | ||
| return makeTupleType(leftParen: makeLeftParenToken(), | ||
| elements: makeBlankTupleTypeElementList(), | ||
| rightParen: makeRightParenToken()) | ||
| } | ||
|
|
||
| public static func makeTupleTypeElement(label: TokenSyntax?, | ||
| colon: TokenSyntax?, type: TypeSyntax, | ||
| comma: TokenSyntax?) -> TupleTypeElementSyntax { | ||
| let annotation = makeTypeAnnotation(attributes: makeBlankAttributeList(), | ||
| inOutKeyword: nil, | ||
| type: type) | ||
| return makeTupleTypeElement(label: label, colon: colon, | ||
| typeAnnotation: annotation, | ||
| comma: comma) | ||
| } | ||
|
|
||
| public static func makeTupleTypeElement(type: TypeSyntax, | ||
| comma: TokenSyntax?) -> TupleTypeElementSyntax { | ||
| return makeTupleTypeElement(label: nil, colon: nil, | ||
| type: type, comma: comma) | ||
| } | ||
|
|
||
| public static func makeGenericParameter(type: TypeIdentifierSyntax, | ||
| trailingComma: TokenSyntax) -> GenericParameterSyntax { | ||
| return makeGenericParameter(typeIdentifier: type, colon: nil, | ||
| inheritedType: nil, | ||
| trailingComma: trailingComma) | ||
| } | ||
|
|
||
| public static func makeTypeIdentifier(_ typeName: String, | ||
| leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> TypeIdentifierSyntax { | ||
| let identifier = makeIdentifier(typeName, leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| return makeTypeIdentifier(typeName:identifier, genericArgumentClause: nil, | ||
| period: nil, typeIdentifier: nil) | ||
| } | ||
|
|
||
| public static func makeAnyTypeIdentifier(leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> TypeIdentifierSyntax { | ||
| return makeTypeIdentifier("Any", leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| } | ||
|
|
||
| public static func makeSelfTypeIdentifier(leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> TypeIdentifierSyntax { | ||
| return makeTypeIdentifier("Self", leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| } | ||
|
|
||
| public static func makeTypeToken(leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> TokenSyntax { | ||
| return makeIdentifier("Type", leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| } | ||
|
|
||
| public static func makeProtocolToken(leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> TokenSyntax { | ||
| return makeIdentifier("Protocol", leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| } | ||
|
|
||
| public static func makeEqualityOperator(leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> TokenSyntax { | ||
| return makeToken(.spacedBinaryOperator("=="), | ||
| presence: .present, | ||
| leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| } | ||
|
|
||
| public static func makeStringLiteralExpr(_ text: String, | ||
| leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> StringLiteralExprSyntax { | ||
| let literal = makeStringLiteral("\"\(text)\"", | ||
| leadingTrivia: leadingTrivia, | ||
| trailingTrivia: trailingTrivia) | ||
| return makeStringLiteralExpr(stringLiteral: literal) | ||
| } | ||
|
|
||
| public static func makeVariableExpr(_ text: String, | ||
| leadingTrivia: Trivia = [], | ||
| trailingTrivia: Trivia = []) -> SymbolicReferenceExprSyntax { | ||
| let string = makeIdentifier(text, | ||
| leadingTrivia: leadingTrivia, trailingTrivia: trailingTrivia) | ||
| return makeSymbolicReferenceExpr(identifier: string, | ||
| genericArgumentClause: nil) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| %{ | ||
| from gyb_syntax_support import * | ||
| from gyb_syntax_support.kinds import SYNTAX_BASE_KINDS | ||
| grouped_nodes = { kind: [] for kind in SYNTAX_BASE_KINDS } | ||
| for node in SYNTAX_NODES: | ||
| grouped_nodes[node.base_kind].append(node) | ||
| # -*- mode: Swift -*- | ||
| # Ignore the following admonition; it applies to the resulting .swift file only | ||
| }% | ||
| //// Automatically Generated From SyntaxKind.swift.gyb. | ||
| //// Do Not Edit Directly! | ||
| //===--------------- SyntaxKind.swift - Syntax Kind definitions -----------===// | ||
| // | ||
| // 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 | ||
|
|
||
| /// Enumerates the known kinds of Syntax represented in the Syntax tree. | ||
| internal enum SyntaxKind: String, Codable { | ||
| case token = "Token" | ||
| case unknown = "Unknown" | ||
| % for node in SYNTAX_NODES: | ||
| case ${node.swift_syntax_kind} = "${node.syntax_kind}" | ||
| % end | ||
|
|
||
| % for name, nodes in grouped_nodes.items(): | ||
| % if name not in ["Syntax", "SyntaxCollection"]: | ||
| /// Whether the underlying kind is a sub-kind of ${name}Syntax. | ||
| public var is${name}: Bool { | ||
| switch self { | ||
| % for node in nodes: | ||
| case .${node.swift_syntax_kind}: return true | ||
| % end | ||
| default: return false | ||
| } | ||
| } | ||
| % end | ||
| % end | ||
|
|
||
| public init(from decoder: Decoder) throws { | ||
| let container = try decoder.singleValueContainer() | ||
| let kind = try container.decode(String.self) | ||
| self = SyntaxKind(rawValue: kind) ?? .unknown | ||
| } | ||
| } | ||
|
|
||
| extension Syntax { | ||
| /// Creates a Syntax node from the provided RawSyntax using the appropriate | ||
| /// Syntax type, as specified by its kind. | ||
| /// - Parameters: | ||
| /// - raw: The raw syntax with which to create this node. | ||
| /// - root: The root of this tree, or `nil` if the new node is the root. | ||
| static func fromRaw(_ raw: RawSyntax) -> Syntax { | ||
| let data = SyntaxData(raw: raw) | ||
| return make(root: nil, data: data) | ||
| } | ||
|
|
||
| /// Creates a Syntax node from the provided SyntaxData using the appropriate | ||
| /// Syntax type, as specified by its kind. | ||
| /// - Parameters: | ||
| /// - root: The root of this tree, or `nil` if the new node is the root. | ||
| /// - data: The data for this new node. | ||
| static func make(root: SyntaxData?, data: SyntaxData) -> Syntax { | ||
| let root = root ?? data | ||
| switch data.raw.kind { | ||
| case .token: return TokenSyntax(root: root, data: data) | ||
| case .unknown: return Syntax(root: root, data: data) | ||
| % for node in SYNTAX_NODES: | ||
| case .${node.swift_syntax_kind}: return ${node.name}(root: root, data: data) | ||
| % end | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| %{ | ||
| # -*- mode: Swift -*- | ||
| from gyb_syntax_support import * | ||
| NODE_MAP = create_node_map() | ||
| # Ignore the following admonition it applies to the resulting .swift file only | ||
| }% | ||
| //// Automatically Generated From SyntaxNodes.swift.gyb. | ||
| //// Do Not Edit Directly! | ||
| //===------------ SyntaxNodes.swift - Syntax Node definitions -------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| %{ | ||
| """ | ||
| Each Syntax node is a subclass of a more generic node. For example, | ||
| StructDeclSyntax is a subclass of DeclSyntax and can be used in contexts | ||
| where DeclSyntax is expected. | ||
| Each node will have: | ||
| - An accessor for each child that will lazily instantiate it. | ||
| - A `withX(_ x: XSyntax)` method for each child that will return a new Syntax | ||
| node with the existing X child replaced with the passed-in version. This is a | ||
| way to incrementally transform nodes in the tree. | ||
| - An `addX(_ x: XSyntax)` method for children that are collections. This will | ||
| append the provided node to the collection and return a new node with that | ||
| collection replaced. | ||
| - (in DEBUG mode) a `validate()` method that's called in the initializer. This | ||
| only validates that all the children are the right kind/token, and does not | ||
| raise an error if, say, a non-optional child is missing. | ||
| """ | ||
| }% | ||
|
|
||
| % for node in SYNTAX_NODES: | ||
| % base_type = node.base_type | ||
| % if node.collection_element: | ||
| public typealias ${node.name} = SyntaxCollection<${node.collection_element_type}> | ||
| % else: | ||
| public class ${node.name}: ${base_type} { | ||
| % if node.children: | ||
| enum Cursor: Int { | ||
| % for child in node.children: | ||
| case ${child.swift_name} | ||
| % end | ||
| } | ||
| % end | ||
|
|
||
| % if node.requires_validation(): | ||
| #if DEBUG | ||
| override func validate() { | ||
| if isMissing { return } | ||
| precondition(raw.layout.count == ${len(node.children)}) | ||
| % for child in node.children: | ||
| % child_var = '_' + child.swift_name | ||
| let ${child_var} = raw[Cursor.${child.swift_syntax_kind}] | ||
| % if child.token_choices: | ||
| % choices = ["." + choice.swift_kind() for choice in child.token_choices] | ||
| % choice_array = "[%s]" % ', '.join(choices) | ||
| guard let ${child_var}TokenKind = ${child_var}.tokenKind else { | ||
| fatalError("expected token child, got \(${child_var}.kind)") | ||
| } | ||
| precondition(${choice_array}.contains(${child_var}TokenKind), | ||
| "expected one of ${choice_array} for '${child.swift_name}' " + | ||
| "in node of kind ${node.swift_syntax_kind}") | ||
| % elif child.text_choices: | ||
| % choices = ", ".join("\"%s\"" % choice | ||
| % for choice in child.text_choices) | ||
| guard let ${child_var}TokenKind = ${child_var}.tokenKind else { | ||
| fatalError("expected token child, got \(${child_var}.kind)") | ||
| } | ||
| precondition([${choices}].contains(${child_var}TokenKind.text), | ||
| "expected one of '[${', '.join(child.text_choices)}]', got " + | ||
| "'\(${child_var}TokenKind.text)'") | ||
| % else: | ||
| precondition(${child_var}.kind == .${child.swift_syntax_kind}, | ||
| "expected child of kind .${child.swift_syntax_kind}, " + | ||
| "got \(${child_var}.kind)") | ||
| % end | ||
| % end | ||
| } | ||
| #endif | ||
| % end | ||
| % for child in node.children: | ||
|
|
||
| % if child.is_optional: | ||
| public var ${child.swift_name}: ${child.type_name}? { | ||
| guard raw[Cursor.${child.swift_name}].isPresent else { return nil } | ||
| return ${child.type_name}(root: _root, | ||
| data: data.cachedChild(at: Cursor.${child.swift_name})) | ||
| } | ||
| % else: | ||
| public var ${(child.swift_name)}: ${child.type_name} { | ||
| return ${child.type_name}(root: _root, | ||
| data: data.cachedChild(at: Cursor.${child.swift_name})) | ||
| } | ||
| % end | ||
| % child_node = NODE_MAP.get(child.syntax_kind) | ||
| % if child_node and child_node.is_syntax_collection(): | ||
| % child_elt = child_node.collection_element_name | ||
| % child_elt_type = child_node.collection_element_type | ||
|
|
||
| public func add${child_elt}(_ elt: ${child_elt_type}) -> ${node.name} { | ||
| let childRaw = raw[Cursor.${child.swift_name}].appending(elt.raw) | ||
| let (root, newData) = data.replacingChild(childRaw, | ||
| at: Cursor.${child.swift_name}) | ||
| return ${node.name}(root: root, data: newData) | ||
| } | ||
| % end | ||
|
|
||
| public func with${child.name}( | ||
| _ new${child.type_name}: ${child.type_name}?) -> ${node.name} { | ||
| let raw = new${child.type_name}?.raw ?? ${make_missing_swift_child(child)} | ||
| let (root, newData) = data.replacingChild(raw, | ||
| at: Cursor.${child.swift_name}) | ||
| return ${node.name}(root: root, data: newData) | ||
| } | ||
| % end | ||
| } | ||
| % end | ||
| % end | ||
|
|
||
| /// MARK: Convenience methods | ||
|
|
||
| extension StructDeclSyntax { | ||
| func withIdentifier(_ name: String) -> StructDeclSyntax { | ||
| let newToken = SyntaxFactory.makeIdentifier(name, | ||
| leadingTrivia: identifier.leadingTrivia, | ||
| trailingTrivia: identifier.trailingTrivia) | ||
| return withIdentifier(newToken) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| %{ | ||
| from gyb_syntax_support import * | ||
| # -*- mode: Swift -*- | ||
| # Ignore the following admonition it applies to the resulting .swift file only | ||
| def is_visitable(node): | ||
| return not node.is_base() and not node.collection_element | ||
| }% | ||
| //// Automatically Generated From SyntaxFactory.swift.gyb. | ||
| //// Do Not Edit Directly! | ||
| //===------------ SyntaxRewriter.swift - Syntax Rewriter class ------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file defines the SyntaxRewriter, a class that performs a standard walk | ||
| // and tree-rebuilding pattern. | ||
| // | ||
| // Subclassers of this class can override the walking behavior for any syntax | ||
| // node and transform nodes however they like. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| open class SyntaxRewriter { | ||
| public init() {} | ||
| % for node in SYNTAX_NODES: | ||
| % if is_visitable(node): | ||
| open func visit(_ node: ${node.name}) -> ${node.base_type} { | ||
| % cast = ('as! ' + node.base_type) if node.base_type != 'Syntax' else '' | ||
| return visitChildren(node)${cast} | ||
| } | ||
|
|
||
| % end | ||
| % end | ||
|
|
||
| open func visit(_ token: TokenSyntax) -> Syntax { | ||
| return token | ||
| } | ||
| public func visit(_ node: Syntax) -> Syntax { | ||
| switch node.raw.kind { | ||
| case .token: return visit(node as! TokenSyntax) | ||
| % for node in SYNTAX_NODES: | ||
| % if is_visitable(node): | ||
| case .${node.swift_syntax_kind}: return visit(node as! ${node.name}) | ||
| % end | ||
| % end | ||
| default: return visitChildren(node) | ||
| } | ||
| } | ||
|
|
||
| func visitChildren(_ node: Syntax) -> Syntax { | ||
| let newLayout = node.children.map { visit($0).raw } | ||
| return Syntax.fromRaw(node.raw.replacingLayout(newLayout)) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| %{ | ||
| # -*- mode: Swift -*- | ||
| from gyb_syntax_support import * | ||
| # Ignore the following admonition it applies to the resulting .swift file only | ||
| }% | ||
| //// Automatically Generated From TokenKind.swift.gyb. | ||
| //// Do Not Edit Directly! | ||
| //===----------------- TokenKind.swift - Token Kind Enum ------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| /// Enumerates the kinds of tokens in the Swift language. | ||
| public enum TokenKind: Codable { | ||
| case unknown | ||
| case eof | ||
| % for token in SYNTAX_TOKENS: | ||
| % kind = token.swift_kind() | ||
| % | ||
| % # Tokens that don't have a set text have an associated value that | ||
| % # contains their text. | ||
| % if not token.text: | ||
| % kind += '(String)' | ||
| % end | ||
| case ${kind} | ||
| % end | ||
|
|
||
| /// The textual representation of this token kind. | ||
| var text: String { | ||
| switch self { | ||
| case .unknown: return "unknown" | ||
| case .eof: return "" | ||
| % for token in SYNTAX_TOKENS: | ||
| % if token.text: | ||
| case .${token.swift_kind()}: return "${token.text}" | ||
| % else: | ||
| case .${token.swift_kind()}(let text): return text | ||
| % end | ||
| % end | ||
| } | ||
| } | ||
|
|
||
| /// Keys for serializing and deserializing token kinds. | ||
| enum CodingKeys: String, CodingKey { | ||
| case kind, text | ||
| } | ||
|
|
||
| public init(from decoder: Decoder) throws { | ||
| let container = try decoder.container(keyedBy: CodingKeys.self) | ||
| let kind = try container.decode(String.self, forKey: .kind) | ||
| switch kind { | ||
| case "unknown": self = .unknown | ||
| case "eof": self = .eof | ||
| % for token in SYNTAX_TOKENS: | ||
| case "${token.kind}": | ||
| % if token.text: | ||
| self = .${token.swift_kind()} | ||
| % else: | ||
| let text = try container.decode(String.self, forKey: .text) | ||
| self = .${token.swift_kind()}(text) | ||
| % end | ||
| % end | ||
| default: fatalError("unknown token kind \(kind)") | ||
| } | ||
| } | ||
|
|
||
| public func encode(to encoder: Encoder) throws { | ||
| var container = encoder.container(keyedBy: CodingKeys.self) | ||
| try container.encode(kind, forKey: .kind) | ||
| try container.encode(text, forKey: .text) | ||
| } | ||
|
|
||
| var kind: String { | ||
| switch self { | ||
| case .unknown: return "unknown" | ||
| case .eof: return "eof" | ||
| % for token in SYNTAX_TOKENS: | ||
| % kind = token.swift_kind() | ||
| % if not token.text: | ||
| % kind += '(_)' | ||
| % end | ||
| case .${kind}: return "${token.kind}" | ||
| % end | ||
| } | ||
| } | ||
| } | ||
|
|
||
| extension TokenKind: Equatable { | ||
| public static func ==(lhs: TokenKind, rhs: TokenKind) -> Bool { | ||
| switch (lhs, rhs) { | ||
| case (.unknown, .unknown): return true | ||
| case (.eof, .eof): return true | ||
| % for token in SYNTAX_TOKENS: | ||
| % kind = token.swift_kind() | ||
| % if token.text: | ||
| case (.${kind}, .${kind}): return true | ||
| % else: | ||
| case (.${kind}(let lhsText), .${kind}(let rhsText)): | ||
| return lhsText == rhsText | ||
| % end | ||
| % end | ||
| default: return false | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,283 @@ | ||
| //===------------------- Trivia.swift - Source Trivia Enum ----------------===// | ||
| // | ||
| // 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 contiguous stretch of a single kind of trivia. The constituent part of | ||
| /// a `Trivia` collection. | ||
| /// | ||
| /// For example, four spaces would be represented by | ||
| /// `.spaces(4)` | ||
| /// | ||
| /// In general, you should deal with the actual Trivia collection instead | ||
| /// of individual pieces whenever possible. | ||
| public enum TriviaPiece: Codable { | ||
| enum CodingKeys: CodingKey { | ||
| case kind, value | ||
| } | ||
| public init(from decoder: Decoder) throws { | ||
| let container = try decoder.container(keyedBy: CodingKeys.self) | ||
| let kind = try container.decode(String.self, forKey: .kind) | ||
| switch kind { | ||
| case "Space": | ||
| let value = try container.decode(Int.self, forKey: .value) | ||
| self = .spaces(value) | ||
| case "Tab": | ||
| let value = try container.decode(Int.self, forKey: .value) | ||
| self = .tabs(value) | ||
| case "VerticalTab": | ||
| let value = try container.decode(Int.self, forKey: .value) | ||
| self = .verticalTabs(value) | ||
| case "Formfeed": | ||
| let value = try container.decode(Int.self, forKey: .value) | ||
| self = .formfeeds(value) | ||
| case "Newline": | ||
| let value = try container.decode(Int.self, forKey: .value) | ||
| self = .newlines(value) | ||
| case "Backtick": | ||
| let value = try container.decode(Int.self, forKey: .value) | ||
| self = .backticks(value) | ||
| case "LineComment": | ||
| let value = try container.decode(String.self, forKey: .value) | ||
| self = .lineComment(value) | ||
| case "BlockComment": | ||
| let value = try container.decode(String.self, forKey: .value) | ||
| self = .blockComment(value) | ||
| case "DocLineComment": | ||
| let value = try container.decode(String.self, forKey: .value) | ||
| self = .docLineComment(value) | ||
| case "DocBlockComment": | ||
| let value = try container.decode(String.self, forKey: .value) | ||
| self = .docLineComment(value) | ||
| default: | ||
| let context = | ||
| DecodingError.Context(codingPath: [CodingKeys.kind], | ||
| debugDescription: "invalid TriviaPiece kind \(kind)") | ||
| throw DecodingError.valueNotFound(String.self, context) | ||
| } | ||
| } | ||
|
|
||
| public func encode(to encoder: Encoder) throws { | ||
| var container = encoder.container(keyedBy: CodingKeys.self) | ||
| switch self { | ||
| case .blockComment(let comment): | ||
| try container.encode("BlockComment", forKey: .kind) | ||
| try container.encode(comment, forKey: .value) | ||
| case .docBlockComment(let comment): | ||
| try container.encode("DocBlockComment", forKey: .kind) | ||
| try container.encode(comment, forKey: .value) | ||
| case .docLineComment(let comment): | ||
| try container.encode("DocLineComment", forKey: .kind) | ||
| try container.encode(comment, forKey: .value) | ||
| case .lineComment(let comment): | ||
| try container.encode("LineComment", forKey: .kind) | ||
| try container.encode(comment, forKey: .value) | ||
| case .formfeeds(let count): | ||
| try container.encode("Formfeed", forKey: .kind) | ||
| try container.encode(count, forKey: .value) | ||
| case .backticks(let count): | ||
| try container.encode("Backtick", forKey: .kind) | ||
| try container.encode(count, forKey: .value) | ||
| case .newlines(let count): | ||
| try container.encode("Newline", forKey: .kind) | ||
| try container.encode(count, forKey: .value) | ||
| case .spaces(let count): | ||
| try container.encode("Space", forKey: .kind) | ||
| try container.encode(count, forKey: .value) | ||
| case .tabs(let count): | ||
| try container.encode("Tab", forKey: .kind) | ||
| try container.encode(count, forKey: .value) | ||
| case .verticalTabs(let count): | ||
| try container.encode("VerticalTab", forKey: .kind) | ||
| try container.encode(count, forKey: .value) | ||
|
|
||
| } | ||
| } | ||
|
|
||
| /// A space ' ' character. | ||
| case spaces(Int) | ||
|
|
||
| /// A tab '\t' character. | ||
| case tabs(Int) | ||
|
|
||
| /// A vertical tab '\v' character. | ||
| case verticalTabs(Int) | ||
|
|
||
| /// A form-feed '\f' character. | ||
| case formfeeds(Int) | ||
|
|
||
| /// A newline '\n' character. | ||
| case newlines(Int) | ||
|
|
||
| /// A backtick '`' character, used to escape identifiers. | ||
| case backticks(Int) | ||
|
|
||
| /// A developer line comment, starting with '//' | ||
| case lineComment(String) | ||
|
|
||
| /// A developer block comment, starting with '/*' and ending with '*/'. | ||
| case blockComment(String) | ||
|
|
||
| /// A documentation line comment, starting with '///'. | ||
| case docLineComment(String) | ||
|
|
||
| /// A documentation block comment, starting with '/**' and ending with '*/. | ||
| case docBlockComment(String) | ||
| } | ||
|
|
||
| extension TriviaPiece: TextOutputStreamable { | ||
| /// Prints the provided trivia as they would be written in a source file. | ||
| /// | ||
| /// - Parameter stream: The stream to which to print the trivia. | ||
| public func write<Target>(to target: inout Target) | ||
| where Target: TextOutputStream { | ||
| func printRepeated(_ character: String, count: Int) { | ||
| for _ in 0..<count { target.write(character) } | ||
| } | ||
| switch self { | ||
| case let .spaces(count): printRepeated(" ", count: count) | ||
| case let .tabs(count): printRepeated("\t", count: count) | ||
| case let .verticalTabs(count): printRepeated("\u{2B7F}", count: count) | ||
| case let .formfeeds(count): printRepeated("\u{240C}", count: count) | ||
| case let .newlines(count): printRepeated("\n", count: count) | ||
| case let .backticks(count): printRepeated("`", count: count) | ||
| case let .lineComment(text), | ||
| let .blockComment(text), | ||
| let .docLineComment(text), | ||
| let .docBlockComment(text): | ||
| target.write(text) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A collection of leading or trailing trivia. This is the main data structure | ||
| /// for thinking about trivia. | ||
| public struct Trivia: Codable { | ||
| let pieces: [TriviaPiece] | ||
|
|
||
| /// Creates Trivia with the provided underlying pieces. | ||
| public init(pieces: [TriviaPiece]) { | ||
| self.pieces = pieces | ||
| } | ||
|
|
||
| public init(from decoder: Decoder) throws { | ||
| var container = try decoder.unkeyedContainer() | ||
| var pieces = [TriviaPiece]() | ||
| while let piece = try container.decodeIfPresent(TriviaPiece.self) { | ||
| pieces.append(piece) | ||
| } | ||
| self.pieces = pieces | ||
| } | ||
|
|
||
| public func encode(to encoder: Encoder) throws { | ||
| var container = encoder.unkeyedContainer() | ||
| for piece in pieces { | ||
| try container.encode(piece) | ||
| } | ||
| } | ||
|
|
||
| /// Creates Trivia with no pieces. | ||
| public static var zero: Trivia { | ||
| return Trivia(pieces: []) | ||
| } | ||
|
|
||
| /// Creates a new `Trivia` by appending the provided `TriviaPiece` to the end. | ||
| public func appending(_ piece: TriviaPiece) -> Trivia { | ||
| var copy = pieces | ||
| copy.append(piece) | ||
| return Trivia(pieces: copy) | ||
| } | ||
|
|
||
| /// Return a piece of trivia for some number of space characters in a row. | ||
| public static func spaces(_ count: Int) -> Trivia { | ||
| return [.spaces(count)] | ||
| } | ||
|
|
||
| /// Return a piece of trivia for some number of tab characters in a row. | ||
| public static func tabs(_ count: Int) -> Trivia { | ||
| return [.tabs(count)] | ||
| } | ||
|
|
||
| /// A vertical tab '\v' character. | ||
| public static func verticalTabs(_ count: Int) -> Trivia { | ||
| return [.verticalTabs(count)] | ||
| } | ||
|
|
||
| /// A form-feed '\f' character. | ||
| public static func formfeeds(_ count: Int) -> Trivia { | ||
| return [.formfeeds(count)] | ||
| } | ||
|
|
||
| /// Return a piece of trivia for some number of newline characters | ||
| /// in a row. | ||
| public static func newlines(_ count: Int) -> Trivia { | ||
| return [.newlines(count)] | ||
| } | ||
|
|
||
| /// Return a piece of trivia for some number of backtick '`' characters | ||
| /// in a row. | ||
| public static func backticks(_ count: Int) -> Trivia { | ||
| return [.backticks(count)] | ||
| } | ||
|
|
||
| /// Return a piece of trivia for a single line of ('//') developer comment. | ||
| public static func lineComment(_ text: String) -> Trivia { | ||
| return [.lineComment(text)] | ||
| } | ||
|
|
||
| /// Return a piece of trivia for a block comment ('/* ... */') | ||
| public static func blockComment(_ text: String) -> Trivia { | ||
| return [.blockComment(text)] | ||
| } | ||
|
|
||
| /// Return a piece of trivia for a single line of ('///') doc comment. | ||
| public static func docLineComment(_ text: String) -> Trivia { | ||
| return [.docLineComment(text)] | ||
| } | ||
|
|
||
| /// Return a piece of trivia for a documentation block comment ('/** ... */') | ||
| public static func docBlockComment(_ text: String) -> Trivia { | ||
| return [.docBlockComment(text)] | ||
| } | ||
| } | ||
|
|
||
| /// Conformance for Trivia to the Collection protocol. | ||
| extension Trivia: Collection { | ||
| public var startIndex: Int { | ||
| return pieces.startIndex | ||
| } | ||
|
|
||
| public var endIndex: Int { | ||
| return pieces.endIndex | ||
| } | ||
|
|
||
| public func index(after i: Int) -> Int { | ||
| return pieces.index(after: i) | ||
| } | ||
|
|
||
| public subscript(_ index: Int) -> TriviaPiece { | ||
| return pieces[index] | ||
| } | ||
| } | ||
|
|
||
|
|
||
| extension Trivia: ExpressibleByArrayLiteral { | ||
| /// Creates Trivia from the provided pieces. | ||
| public init(arrayLiteral elements: TriviaPiece...) { | ||
| self.pieces = elements | ||
| } | ||
| } | ||
|
|
||
| /// Concatenates two collections of `Trivia` into one collection. | ||
| public func +(lhs: Trivia, rhs: Trivia) -> Trivia { | ||
| return Trivia(pieces: lhs.pieces + rhs.pieces) | ||
| } |