blob: 4d8f363d757a49f260ea498625225eaf2965d51b [file] [log] [blame]
%{
# -*- 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
}
}
}