| %{ |
| # -*- mode: Swift -*- |
| from gyb_syntax_support.Trivia import TRIVIAS |
| # Ignore the following admonition it applies to the resulting .swift file only |
| }% |
| //// Automatically Generated From Trivia.swift.gyb. |
| //// Do Not Edit Directly! |
| //===------------------- 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 { |
| % for trivia in TRIVIAS: |
| case "${trivia.name}": |
| % if trivia.is_collection(): |
| let value = try container.decode(Int.self, forKey: .value) |
| self = .${trivia.lower_name}s(value) |
| % else: |
| let value = try container.decode(String.self, forKey: .value) |
| self = .${trivia.lower_name}(value) |
| % end |
| % end |
| 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 { |
| % for trivia in TRIVIAS: |
| % if trivia.is_collection(): |
| case .${trivia.lower_name}s(let count): |
| try container.encode("${trivia.name}", forKey: .kind) |
| try container.encode(count, forKey: .value) |
| % else: |
| case .${trivia.lower_name}(let text): |
| try container.encode("${trivia.name}", forKey: .kind) |
| try container.encode(text, forKey: .value) |
| % end |
| % end |
| } |
| } |
| |
| % for trivia in TRIVIAS: |
| /// ${trivia.comment} |
| % if trivia.is_collection(): |
| case ${trivia.lower_name}s(Int) |
| % else: |
| case ${trivia.lower_name}(String) |
| % end |
| % end |
| } |
| |
| 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 { |
| % for trivia in TRIVIAS: |
| % if trivia.is_collection(): |
| % joined = ''.join(trivia.swift_characters) |
| case let .${trivia.lower_name}s(count): |
| printRepeated("${joined}", count: count) |
| % else: |
| case let .${trivia.lower_name}(text): |
| target.write(text) |
| % end |
| % end |
| } |
| } |
| |
| /// Computes the information from this trivia to inform the source locations |
| /// of the associated tokens. |
| /// Specifically, walks through the trivia and keeps track of every newline |
| /// to give a number of how many newlines and UTF8 characters appear in the |
| /// trivia, along with the UTF8 offset of the last column. |
| func characterSizes() -> (lines: Int, lastColumn: Int, utf8Length: Int) { |
| func calculateTextSizes(_ text: String) -> |
| (lines: Int, lastColumn: Int, utf8Length: Int) { |
| var lines = 0 |
| var col = 0 |
| var total = 0 |
| var prevChar: UInt8? = nil |
| // TODO: CR + LF should be regarded as one newline |
| for char in text.utf8 { |
| total += 1 |
| switch char { |
| case 0x0a: |
| if prevChar == 0x0d { |
| /* ASCII CR LF */ |
| assert(col == 0) |
| } else { |
| /* ASCII newline */ |
| col = 0 |
| lines += 1 |
| } |
| /* ASCII carriage-return */ |
| case 0x0d: |
| col = 0 |
| lines += 1 |
| |
| default: |
| col += 1 |
| } |
| prevChar = char |
| } |
| return (lines: lines, lastColumn: col, utf8Length: total) |
| } |
| switch self { |
| % for trivia in TRIVIAS: |
| % if trivia.is_new_line: |
| case let .${trivia.lower_name}s(n): |
| return (lines: n, lastColumn: 0, utf8Length: n * ${trivia.characters_len()}) |
| % elif trivia.is_collection(): |
| case let .${trivia.lower_name}s(n): |
| return (lines: 0, lastColumn: n, utf8Length: n * ${trivia.characters_len()}) |
| % else: |
| case let .${trivia.lower_name}(text): |
| return calculateTextSizes(text) |
| % end |
| % end |
| } |
| } |
| } |
| |
| /// 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) |
| } |
| |
| % for trivia in TRIVIAS: |
| % if trivia.is_collection(): |
| % joined = ''.join(trivia.swift_characters) |
| /// Return a piece of trivia for some number of '${joined}' characters. |
| public static func ${trivia.lower_name}s(_ count: Int) -> Trivia { |
| return [.${trivia.lower_name}s(count)] |
| } |
| % else: |
| /// Return a piece of trivia for ${trivia.name}. |
| public static func ${trivia.lower_name}(_ text: String) -> Trivia { |
| return [.${trivia.lower_name}(text)] |
| } |
| % end |
| % end |
| |
| /// Computes the total sizes and offsets of all pieces in this Trivia. |
| func characterSizes() -> (lines: Int, lastColumn: Int, utf8Length: Int) { |
| var lines = 0 |
| var lastColumn = 0 |
| var length = 0 |
| for piece in pieces { |
| let (ln, col, len) = piece.characterSizes() |
| lines += ln |
| lastColumn = col |
| length += len |
| } |
| return (lines: lines, lastColumn: lastColumn, utf8Length: length) |
| } |
| } |
| |
| /// 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] |
| } |
| |
| /// Get the byteSize of this trivia |
| public var byteSize: Int { |
| let pos = UTF8Position() |
| for piece in pieces { |
| piece.accumulateAbsolutePosition(pos) |
| } |
| return pos.byteOffset |
| } |
| } |
| |
| |
| 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) |
| } |
| |
| extension TriviaPiece { |
| func accumulateAbsolutePosition(_ pos: AbsolutePosition) { |
| switch self { |
| % for trivia in TRIVIAS: |
| % if trivia.is_new_line: |
| case let .${trivia.lower_name}s(count): |
| pos.add(lines: count, size: ${trivia.characters_len()}) |
| % elif trivia.is_collection(): |
| case let .${trivia.lower_name}s(count): |
| pos.add(columns: count) |
| % else: |
| case let .${trivia.lower_name}(text): |
| pos.add(text: text) |
| % end |
| % end |
| } |
| } |
| } |