Skip to content

Commit 6963709

Browse files
committed
Add typed rawsyntax types
* RawXXXSyntax is a typed RawSyntax providing SyntaxKind specific API including a factory initializer. * Expose RawSyntax, RawXXXSyntax, and other related types as SPI (RawSyntax)
1 parent 71ab8cc commit 6963709

File tree

13 files changed

+14563
-23
lines changed

13 files changed

+14563
-23
lines changed

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ let package = Package(
5555
dependencies: ["_CSwiftSyntax"],
5656
exclude: [
5757
"Misc.swift.gyb",
58+
"RawSyntaxNodes.swift.gyb",
5859
"SyntaxAnyVisitor.swift.gyb",
5960
"SyntaxBaseNodes.swift.gyb",
6061
"SyntaxClassification.swift.gyb",

Sources/SwiftSyntax/RawSyntax.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ extension RawSyntaxData.MaterializedToken {
5454
/// Represents the raw tree structure underlying the syntax tree. These nodes
5555
/// have no notion of identity and only provide structure to the tree. They
5656
/// are immutable and can be freely shared between syntax nodes.
57-
struct RawSyntax {
57+
@_spi(RawSyntax)
58+
public struct RawSyntax {
5859

5960
/// Pointer to the actual data which resides in a SyntaxArena.
6061
var pointer: UnsafePointer<RawSyntaxData>
@@ -514,7 +515,7 @@ extension RawSyntax: TextOutputStreamable, CustomStringConvertible {
514515
/// Prints the RawSyntax node, and all of its children, to the provided
515516
/// stream. This implementation must be source-accurate.
516517
/// - Parameter stream: The stream on which to output this node.
517-
func write<Target: TextOutputStream>(to target: inout Target) {
518+
public func write<Target: TextOutputStream>(to target: inout Target) {
518519
switch rawData.payload {
519520
case .materializedToken(let dat):
520521
for p in dat.leadingTrivia { p.write(to: &target) }
@@ -530,7 +531,7 @@ extension RawSyntax: TextOutputStreamable, CustomStringConvertible {
530531
}
531532

532533
/// A source-accurate description of this node.
533-
var description: String {
534+
public var description: String {
534535
var s = ""
535536
self.write(to: &s)
536537
return s
@@ -892,15 +893,15 @@ extension RawSyntax: CustomDebugStringConvertible {
892893
target.write(")")
893894
}
894895

895-
var debugDescription: String {
896+
public var debugDescription: String {
896897
var string = ""
897898
debugWrite(to: &string, indent: 0, withChildren: false)
898899
return string
899900
}
900901
}
901902

902903
extension RawSyntax: CustomReflectable {
903-
var customMirror: Mirror {
904+
public var customMirror: Mirror {
904905
let mirrorChildren: [Any] = children.map {
905906
child in child ?? (nil as Any?) as Any
906907
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//===------- RawSyntaxNodeProtocol.swift - Typed Raw Syntax Protocol ------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// All typed raw syntax nodes conform to this protocol.
14+
/// `RawXXXSyntax` is a typed wrappeer of `RawSyntax`.
15+
@_spi(RawSyntax)
16+
public protocol RawSyntaxNodeProtocol {
17+
/// Returns `true` if `raw` can be cast to this concrete raw syntax type.
18+
static func isKindOf(_ raw: RawSyntax) -> Bool
19+
20+
/// Type erased raw syntax.
21+
var raw: RawSyntax { get }
22+
23+
/// Create the typed raw syntax if `other` can be cast to `Self`
24+
init?<T: RawSyntaxNodeProtocol>(_ other: T)
25+
}
26+
27+
public extension RawSyntaxNodeProtocol {
28+
/// Cast to the specified raw syntax type if possible.
29+
func `as`<Node: RawSyntaxNodeProtocol>(_: Node.Type) -> Node? {
30+
Node(self)
31+
}
32+
33+
/// Check if this instance can be cast to the specified syntax type.
34+
func `is`<Node: RawSyntaxNodeProtocol>(_: Node.Type) -> Bool {
35+
Node.isKindOf(self.raw)
36+
}
37+
}
38+
39+
/// `RawSyntax` itself conforms to `RawSyntaxNodeProtocol`.
40+
extension RawSyntax: RawSyntaxNodeProtocol {
41+
public static func isKindOf(_ raw: RawSyntax) -> Bool {
42+
return true
43+
}
44+
public var raw: RawSyntax { self }
45+
46+
init(raw: RawSyntax) {
47+
self = raw
48+
}
49+
50+
public init<T: RawSyntaxNodeProtocol>(_ other: T) {
51+
self.init(raw: other.raw)
52+
}
53+
}
54+
55+
@_spi(RawSyntax)
56+
public struct RawTokenSyntax: RawSyntaxNodeProtocol {
57+
public static func isKindOf(_ raw: RawSyntax) -> Bool {
58+
return raw.kind == .token
59+
}
60+
61+
public var raw: RawSyntax
62+
63+
init(raw: RawSyntax) {
64+
assert(Self.isKindOf(raw))
65+
self.raw = raw
66+
}
67+
68+
public init?<Node: RawSyntaxNodeProtocol>(_ other: Node) {
69+
guard Self.isKindOf(other.raw) else { return nil }
70+
self.init(raw: other.raw)
71+
}
72+
73+
public var tokenKind: RawTokenKind {
74+
return raw.rawTokenKind
75+
}
76+
77+
public var tokenText: SyntaxText {
78+
return raw.rawTokenText
79+
}
80+
81+
public var presence: SourcePresence {
82+
raw.presence
83+
}
84+
85+
public var isMissing: Bool {
86+
presence == .missing
87+
}
88+
89+
/// Creates a `TokenSyntax`. `text` and trivia must be managed by the same
90+
/// `arena`.
91+
public init(
92+
kind: RawTokenKind,
93+
text: SyntaxText,
94+
leadingTriviaPieces: [RawTriviaPiece],
95+
trailingTriviaPieces: [RawTriviaPiece],
96+
arena: SyntaxArena
97+
) {
98+
assert(arena.contains(text: text), "token text must be managed by the arena")
99+
let totalTriviaCount = leadingTriviaPieces.count + trailingTriviaPieces.count
100+
let buffer = arena.allocateRawTriviaPieceBuffer(count: totalTriviaCount)
101+
var byteLength = 0
102+
if totalTriviaCount != 0 {
103+
var ptr = buffer.baseAddress!
104+
for piece in leadingTriviaPieces + trailingTriviaPieces {
105+
assert(piece.storedText.map(arena.contains(text:)) ?? true,
106+
"trivia text must be managed by the arena")
107+
byteLength += piece.byteLength
108+
ptr.initialize(to: piece)
109+
ptr = ptr.advanced(by: 1)
110+
}
111+
}
112+
let raw = RawSyntax.materializedToken(
113+
kind: kind, text: text, triviaPieces: RawTriviaPieceBuffer(buffer),
114+
numLeadingTrivia: numericCast(leadingTriviaPieces.count),
115+
byteLength: numericCast(byteLength),
116+
arena: arena)
117+
self = RawTokenSyntax(raw: raw)
118+
}
119+
120+
/// Creates a missing `TokenSyntax` with the specified kind.
121+
public init(missing kind: RawTokenKind, arena: SyntaxArena) {
122+
self.init(
123+
kind: kind, text: "", leadingTriviaPieces: [], trailingTriviaPieces: [],
124+
arena: arena)
125+
}
126+
}
127+
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
%{
2+
from gyb_syntax_support import *
3+
NODE_MAP = create_node_map()
4+
# -*- mode: Swift -*-
5+
# Ignore the following admonition; it applies to the resulting .swift file only
6+
}%
7+
//// Automatically Generated From RawSyntaxNodes.swift.gyb.
8+
//// Do Not Edit Directly!
9+
//===------ RawSyntaxNodes.swift - Typed Raw Syntax Node definitions ------===//
10+
//
11+
// This source file is part of the Swift.org open source project
12+
//
13+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
14+
// Licensed under Apache License v2.0 with Runtime Library Exception
15+
//
16+
// See https://swift.org/LICENSE.txt for license information
17+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
18+
//
19+
//===----------------------------------------------------------------------===//
20+
21+
% for node in SYNTAX_NODES:
22+
% if node.is_base():
23+
@_spi(RawSyntax)
24+
public protocol Raw${node.name}NodeProtocol: Raw${node.base_type}NodeProtocol {}
25+
% end
26+
% end
27+
28+
% for node in SYNTAX_NODES:
29+
30+
@_spi(RawSyntax)
31+
public struct Raw${node.name}: Raw${node.name if node.is_base() else node.base_type}NodeProtocol {
32+
public static func isKindOf(_ raw: RawSyntax) -> Bool {
33+
% if node.is_base():
34+
% sub_kinds = ['.' + n.swift_syntax_kind for n in SYNTAX_NODES if n.base_kind == node.syntax_kind]
35+
switch raw.kind {
36+
case ${', '. join(sub_kinds)}: return true
37+
default: return false
38+
}
39+
% else:
40+
return raw.kind == .${node.swift_syntax_kind}
41+
% end
42+
}
43+
44+
public var raw: RawSyntax
45+
init(raw: RawSyntax) {
46+
assert(Self.isKindOf(raw))
47+
self.raw = raw
48+
}
49+
50+
public init?<Node: RawSyntaxNodeProtocol>(_ other: Node) {
51+
guard Self.isKindOf(other.raw) else { return nil }
52+
self.init(raw: other.raw)
53+
}
54+
55+
% if node.is_base():
56+
public init<Node: Raw${node.name}NodeProtocol>(_ other: Node) {
57+
self.init(raw: other.raw)
58+
}
59+
% end
60+
%
61+
% if node.is_syntax_collection():
62+
public init<C: Collection>(elements: C, arena: SyntaxArena) where C.Element == Raw${node.collection_element_type} {
63+
let raw = RawSyntax.makeLayout(kind: .${node.swift_syntax_kind}, from: elements.map { $0.raw }, arena: arena)
64+
self.init(raw: raw)
65+
}
66+
67+
public var elements: [Raw${node.collection_element_type}] {
68+
raw.children.map { Raw${node.collection_element_type}(raw: $0!) }
69+
}
70+
% end
71+
%
72+
% if node.is_buildable() or node.is_missing():
73+
public init(
74+
% for child in node.children:
75+
% param_label = "_ " if child.is_garbage_nodes() else ""
76+
% optional_mark = "?" if child.is_optional else ""
77+
% param_type = "Raw" + child.type_name + optional_mark
78+
% param_default = " = nil" if child.is_garbage_nodes() else ""
79+
${param_label}${child.swift_name}: ${param_type}${param_default},
80+
% end
81+
arena: SyntaxArena
82+
) {
83+
% if node.children:
84+
let raw = RawSyntax.makeLayout(
85+
kind: .${node.swift_syntax_kind}, uninitializedCount: ${len(node.children)}, arena: arena) { layout in
86+
layout.initialize(repeating: nil)
87+
% for idx, child in enumerate(node.children):
88+
% optional_mark = "?" if child.is_optional else ""
89+
layout[${idx}] = ${child.swift_name}${optional_mark}.raw
90+
% end
91+
}
92+
% else:
93+
let raw = RawSyntax.makeEmptyLayout(kind: .${node.swift_syntax_kind}, arena: arena)
94+
% end
95+
self.init(raw: raw)
96+
}
97+
98+
% for idx, child in enumerate(node.children):
99+
% optional_mark = "?" if child.is_optional else ""
100+
% iuo_mark = "!" if not child.is_optional else ""
101+
public var ${child.swift_name}: Raw${child.type_name + optional_mark} {
102+
% if child.type_name == "Syntax":
103+
raw.children[${idx}]${iuo_mark}
104+
% else:
105+
raw.children[${idx}].map(Raw${child.type_name}.init(raw:))${iuo_mark}
106+
% end
107+
}
108+
% end
109+
% end
110+
}
111+
% end

Sources/SwiftSyntax/Syntax.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ public struct Syntax: SyntaxProtocol, SyntaxHashable {
2424
self.data = data
2525
}
2626

27+
@_spi(RawSyntax)
28+
public init(raw: RawSyntax) {
29+
self.init(.forRoot(raw))
30+
}
31+
2732
public func _validateLayout() {
2833
// Check the layout of the concrete type
2934
return self.asProtocol(SyntaxProtocol.self)._validateLayout()

Sources/SwiftSyntax/SyntaxArena.swift

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,10 @@ public class SyntaxArena {
7676

7777
/// Copies the contents of a `SyntaxText` to the memory this arena manages,
7878
/// and return the `SyntaxText` in the destiation.
79-
func intern(_ value: SyntaxText) -> SyntaxText {
80-
// Return the passed-in value if it's
81-
// * empty,
82-
// * a part of "source buffer", or
83-
// * in the memory allocated by this arena
84-
if (value.isEmpty || sourceBufferContains(value.baseAddress!) ||
85-
allocator.contains(address: value.baseAddress!)) {
79+
@_spi(RawSyntax)
80+
public func intern(_ value: SyntaxText) -> SyntaxText {
81+
// Return the passed-in value if it's already managed by this arena.
82+
if (self.contains(text: value)) {
8683
return value
8784
}
8885

@@ -141,6 +138,16 @@ public class SyntaxArena {
141138
child === arena || child.contains(arena: arena)
142139
}
143140
}
141+
142+
/// Checks if the given syntax text is managed by this arena.
143+
///
144+
/// "managed" means it's empty, a part of "source buffer", or in the memory
145+
/// allocated by the underlying arena.
146+
func contains(text: SyntaxText) -> Bool {
147+
return (text.isEmpty ||
148+
sourceBufferContains(text.baseAddress!) ||
149+
allocator.contains(address: text.baseAddress!))
150+
}
144151
}
145152

146153
extension SyntaxArena: Hashable {

Sources/SwiftSyntax/SyntaxText.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import Glibc
3131
/// buffer regardless of whether it is a valid UTF8. When creating
3232
/// `Swift.String`, ill-formed UTF8 sequences are replaced with the Unicode
3333
/// replacement character (`\u{FFFD}`).
34-
@_spi(Testing) // SPI name is subject to change
34+
@_spi(RawSyntax)
3535
public struct SyntaxText {
3636
var buffer: UnsafeBufferPointer<UInt8>
3737

@@ -178,7 +178,7 @@ extension String {
178178
///
179179
/// Ill-formed UTF-8 sequences in `syntaxText` are replaced with the Unicode
180180
/// replacement character `\u{FFFD}`.
181-
@_spi(Testing)
181+
@_spi(RawSyntax)
182182
public init(syntaxText: SyntaxText) {
183183
guard !syntaxText.isEmpty else {
184184
self = ""
@@ -196,7 +196,7 @@ extension String {
196196
/// Runs `body` with a `SyntaxText` that refers the contiguous memory of this
197197
/// string. Like `String.withUTF8(_:)`, this may mutates the string if this
198198
/// string was not contiguous.
199-
@_spi(Testing)
199+
@_spi(RawSyntax)
200200
public mutating func withSyntaxText<R>(
201201
_ body: (SyntaxText) throws -> R
202202
) rethrows -> R {

0 commit comments

Comments
 (0)