188 changes: 188 additions & 0 deletions SwiftcInvocation.swift
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)
}
}
217 changes: 217 additions & 0 deletions Syntax.swift
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
}
}
83 changes: 83 additions & 0 deletions SyntaxBuilders.swift.gyb
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
30 changes: 30 additions & 0 deletions SyntaxChildren.swift
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)
}
}
121 changes: 121 additions & 0 deletions SyntaxCollection.swift
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
}
}
199 changes: 199 additions & 0 deletions SyntaxData.swift
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
}
}
205 changes: 205 additions & 0 deletions SyntaxFactory.swift.gyb
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)
}
}
81 changes: 81 additions & 0 deletions SyntaxKind.swift.gyb
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
}
}
}
138 changes: 138 additions & 0 deletions SyntaxNodes.swift.gyb
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)
}
}
61 changes: 61 additions & 0 deletions SyntaxRewriter.swift.gyb
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))
}
}
112 changes: 112 additions & 0 deletions TokenKind.swift.gyb
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
}
}
}
283 changes: 283 additions & 0 deletions Trivia.swift
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)
}