| //===--------------- 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) |
| } |
| } |
| |