1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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
//
//===----------------------------------------------------------------------===//
#if swift(>=6)
@_spi(RawSyntax) public import SwiftSyntax
#else
@_spi(RawSyntax) import SwiftSyntax
#endif
extension Parser {
/// Parse the source code in the given string as Swift source file. See
/// `Parser.init` for more details.
public static func parse(
source: String
) -> SourceFileSyntax {
var parser = Parser(source)
return SourceFileSyntax.parse(from: &parser)
}
/// A compiler interface that allows the enabling of experimental features.
@_spi(ExperimentalLanguageFeatures)
public static func parse(
source: UnsafeBufferPointer<UInt8>,
swiftVersion: SwiftVersion? = nil,
experimentalFeatures: ExperimentalFeatures
) -> SourceFileSyntax {
var parser = Parser(source, swiftVersion: swiftVersion, experimentalFeatures: experimentalFeatures)
return SourceFileSyntax.parse(from: &parser)
}
/// Parse the source code in the given buffer as Swift source file. See
/// `Parser.init` for more details.
public static func parse(
source: UnsafeBufferPointer<UInt8>,
maximumNestingLevel: Int? = nil,
swiftVersion: SwiftVersion? = nil
) -> SourceFileSyntax {
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, swiftVersion: swiftVersion)
return SourceFileSyntax.parse(from: &parser)
}
/// Parse the source code in the given string as Swift source file with support
/// for incremental parsing.
///
/// When parsing a source file for the first time, invoke `parseIncrementally`
/// with `parseTransition: nil`. This returns the initial tree as well as
/// ``LookaheadRanges``. If an edit is made to the source file, an
/// ``IncrementalParseTransition`` can be constructed from the initial tree
/// and its ``LookaheadRanges``. When invoking `parseIncrementally` again with
/// the post-edit source and that parse transition, the parser will re-use
/// nodes that haven’t changed.
///
/// - Parameters:
/// - source: The source code to parse
/// - parseTransition: If a similar source file has already been parsed, the
/// ``IncrementalParseTransition`` that contains the previous tree as well
/// as the edits that were performed to it.
/// - Returns: The parsed tree as well as the ``LookaheadRanges`` that describe
/// how far the parser looked ahead while parsing a node, which is
/// necessary to construct an ``IncrementalParseTransition`` for a
/// subsequent incremental parse
@available(*, deprecated, message: "Use parseIncrementally with `IncrementalParseResult` return instead")
@_disfavoredOverload
public static func parseIncrementally(
source: String,
parseTransition: IncrementalParseTransition?
) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
let parseResult = parseIncrementally(source: source, parseTransition: parseTransition)
return (parseResult.tree, parseResult.lookaheadRanges)
}
/// Parse the source code in the given buffer as Swift source file with support
/// for incremental parsing.
///
/// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)-2gtt2``
@available(*, deprecated, message: "Use parseIncrementally with `IncrementalParseResult` return instead")
@_disfavoredOverload
public static func parseIncrementally(
source: UnsafeBufferPointer<UInt8>,
maximumNestingLevel: Int? = nil,
parseTransition: IncrementalParseTransition?
) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
let parseResult = parseIncrementally(
source: source,
maximumNestingLevel: maximumNestingLevel,
parseTransition: parseTransition
)
return (parseResult.tree, parseResult.lookaheadRanges)
}
/// Parse the source code in the given string as Swift source file with support
/// for incremental parsing.
///
/// When parsing a source file for the first time, invoke `parseIncrementally`
/// with `parseTransition: nil`. This returns the ``IncrementalParseResult``
/// If an edit is made to the source file, an ``IncrementalParseTransition``
/// can be constructed from the ``IncrementalParseResult``.
/// When invoking `parseIncrementally` again with
/// the post-edit source and that parse transition, the parser will re-use
/// nodes that haven’t changed.
///
/// - Parameters:
/// - source: The source code to parse
/// - parseTransition: If a similar source file has already been parsed, the
/// ``IncrementalParseTransition`` that contains the previous tree as well
/// as the edits that were performed to it.
/// - Returns: The ``IncrementalParseResult``, which is
/// necessary to construct an ``IncrementalParseTransition`` for a
/// subsequent incremental parse
public static func parseIncrementally(
source: String,
parseTransition: IncrementalParseTransition?
) -> IncrementalParseResult {
var parser = Parser(source, parseTransition: parseTransition)
return IncrementalParseResult(tree: SourceFileSyntax.parse(from: &parser), lookaheadRanges: parser.lookaheadRanges)
}
/// Parse the source code in the given buffer as Swift source file with support
/// for incremental parsing.
///
/// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)-dj0z``
public static func parseIncrementally(
source: UnsafeBufferPointer<UInt8>,
maximumNestingLevel: Int? = nil,
parseTransition: IncrementalParseTransition?
) -> IncrementalParseResult {
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition)
return IncrementalParseResult(tree: SourceFileSyntax.parse(from: &parser), lookaheadRanges: parser.lookaheadRanges)
}
}
/// The result of incrementally parsing a file.
///
/// This contains the parsed syntax tree and additional information on how far the parser looked ahead to parse each node.
/// This information is required to perform an incremental parse of the tree after applying edits to it.
public struct IncrementalParseResult: Sendable {
/// The syntax tree from parsing source
public let tree: SourceFileSyntax
/// The lookahead ranges for syntax nodes describe
/// how far the parser looked ahead while parsing a node.
public let lookaheadRanges: LookaheadRanges
public init(tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
self.tree = tree
self.lookaheadRanges = lookaheadRanges
}
}
extension Parser {
mutating func parseRemainder<R: RawSyntaxNodeProtocol>(into: R) -> R {
guard !into.raw.kind.isSyntaxCollection, let layout = into.raw.layoutView else {
preconditionFailure("Only support parsing of non-collection layout nodes")
}
let remainingTokens = self.consumeRemainingTokens()
if remainingTokens.isEmpty {
return R.init(transferTrailingTrivaFromEndOfFileIfPresent(raw: into.raw))!
}
let existingUnexpected: [RawSyntax]
if let unexpectedNode = layout.children[layout.children.count - 1] {
precondition(unexpectedNode.is(RawUnexpectedNodesSyntax.self))
existingUnexpected = unexpectedNode.as(RawUnexpectedNodesSyntax.self).elements
} else {
existingUnexpected = []
}
let unexpected = RawUnexpectedNodesSyntax(elements: existingUnexpected + remainingTokens, arena: self.arena)
let withUnexpected = layout.replacingChild(at: layout.children.count - 1, with: unexpected.raw, arena: self.arena)
return R.init(transferTrailingTrivaFromEndOfFileIfPresent(raw: withUnexpected))!
}
/// Parses the end-of-file token and appends its leading trivia to the provided `RawSyntax`.
/// - Parameter raw: The raw syntax node to which the leading trivia of the end-of-file token will be appended.
/// - Returns: A new `RawSyntax` instance with trailing trivia transferred from the end-of-file token if present, otherwise it will return the raw parameter..
private mutating func transferTrailingTrivaFromEndOfFileIfPresent(raw: RawSyntax) -> RawSyntax {
guard let endOfFileToken = self.consume(if: .endOfFile),
!endOfFileToken.leadingTriviaPieces.isEmpty,
let raw = raw.withTrailingTrivia(
Trivia(
rawPieces: (raw.trailingTriviaPieces ?? []) + endOfFileToken.leadingTriviaPieces
),
arena: self.arena
)
else {
return raw
}
return raw
}
}
private extension Trivia {
init(rawPieces: [RawTriviaPiece]) {
let pieces = rawPieces.map(TriviaPiece.init(raw:))
self.init(pieces: pieces)
}
}
|