Files
swift-mirror/tools/SwiftSyntax/SwiftSyntax.swift
Alex Hoppen 49e9f824bf [SwiftSyntax] Record the nodes that have been reused during an incremental transfer
This way we will be able to avoid reclassifying these nodes for syntax
highlighting since we know they haven't changed.
2018-07-31 15:41:07 -07:00

110 lines
4.0 KiB
Swift

//===--------------- SwiftLanguage.swift - Swift Syntax Library -----------===//
//
// 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 main entry point into the Syntax library.
//===----------------------------------------------------------------------===//
import Foundation
#if os(macOS)
import Darwin
#elseif os(Linux)
import Glibc
#endif
/// A list of possible errors that could be encountered while parsing a
/// Syntax tree.
public enum ParserError: Error, CustomStringConvertible {
case swiftcFailed(Int, String)
case invalidFile
public var description: String {
switch self{
case let .swiftcFailed(exitCode, stderr):
let stderrLines = stderr.split(separator: "\n")
return """
swiftc returned with non-zero exit code \(exitCode)
stderr:
\(stderrLines.joined(separator: "\n "))
"""
case .invalidFile:
return "swiftc created an invalid syntax file"
}
}
}
/// Deserializes the syntax tree from its serialized form to an object tree in
/// Swift. To deserialize incrementally transferred syntax trees, the same
/// instance of the deserializer must be used for all subsequent
/// deserializations.
public final class SyntaxTreeDeserializer {
// FIXME: This lookup table just accumulates nodes, we should invalidate nodes
// that are no longer used at some point and remove them from the table
/// Syntax nodes that have already been parsed and are able to be reused if
/// they were omitted in an incremental syntax tree transfer
private var nodeLookupTable: [SyntaxNodeId: RawSyntax] = [:]
/// The IDs of the nodes that were reused as part of incremental syntax
/// parsing during the last deserialization
public var reusedNodeIds: Set<SyntaxNodeId> = []
public init() {
}
/// Decode a serialized form of SourceFileSyntax to a syntax tree.
/// - Parameter data: The UTF-8 represenation of the serialized syntax tree
/// - Returns: A top-level Syntax node representing the contents of the tree,
/// if the parse was successful.
public func deserialize(_ data: Data) throws -> SourceFileSyntax {
reusedNodeIds = []
let decoder = JSONDecoder()
decoder.userInfo[.rawSyntaxDecodedCallback] = self.addToLookupTable
decoder.userInfo[.omittedNodeLookupFunction] = self.lookupNode
let raw = try decoder.decode(RawSyntax.self, from: data)
guard let file = makeSyntax(raw) as? SourceFileSyntax else {
throw ParserError.invalidFile
}
return file
}
// MARK: Incremental deserialization helper functions
private func lookupNode(id: SyntaxNodeId) -> RawSyntax? {
reusedNodeIds.insert(id)
return nodeLookupTable[id]
}
private func addToLookupTable(_ node: RawSyntax) {
nodeLookupTable[node.id] = node
}
}
/// Namespace for functions to retrieve a syntax tree from the swift compiler
/// and deserializing it.
public enum SyntaxTreeParser {
/// Parses the Swift file at the provided URL into a full-fidelity Syntax tree
/// - Parameter url: The URL you wish to parse.
/// - Returns: A top-level Syntax node representing the contents of the tree,
/// if the parse was successful.
/// - Throws: `ParseError.couldNotFindSwiftc` if `swiftc` could not be
/// located, `ParseError.invalidFile` if the file is invalid.
/// FIXME: Fill this out with all error cases.
public static func parse(_ url: URL) throws -> SourceFileSyntax {
let swiftcRunner = try SwiftcRunner(sourceFile: url)
let result = try swiftcRunner.invoke()
let syntaxTreeData = result.stdoutData
let deserializer = SyntaxTreeDeserializer()
return try deserializer.deserialize(syntaxTreeData)
}
}