Skip to content

Commit 605a5b8

Browse files
committed
Parse shebang as a child of SourceFileSyntax
1 parent b061ee5 commit 605a5b8

27 files changed

+166
-105
lines changed

CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,12 @@ public let DECL_NODES: [Node] = [
19971997
parserFunction: "parseSourceFile",
19981998
traits: ["WithStatements"],
19991999
children: [
2000+
Child(
2001+
name: "Shebang",
2002+
kind: .token(choices: [.token(.shebang)]),
2003+
documentation: "A shebang can specify the path of the compiler when using Swift source file as a script.",
2004+
isOptional: true
2005+
),
20002006
Child(
20012007
name: "Statements",
20022008
kind: .collection(kind: .codeBlockItemList, collectionElementName: "Statement")

CodeGeneration/Sources/SyntaxSupport/TokenSpec.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public enum Token: CaseIterable {
113113
case rightParen
114114
case rightSquare
115115
case semicolon
116+
case shebang
116117
case singleQuote
117118
case stringQuote
118119
case stringSegment
@@ -209,6 +210,8 @@ public enum Token: CaseIterable {
209210
return .punctuator(name: "rightSquare", text: "]")
210211
case .semicolon:
211212
return .punctuator(name: "semicolon", text: ";")
213+
case .shebang:
214+
return .other(name: "shebang", nameForDiagnostics: "shebang")
212215
case .singleQuote:
213216
return .punctuator(name: "singleQuote", text: "\'")
214217
case .stringQuote:

CodeGeneration/Sources/SyntaxSupport/Trivia.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,6 @@ public let TRIVIAS: [Trivia] = [
159159
]
160160
),
161161

162-
Trivia(
163-
name: "Shebang",
164-
comment: #"A script command, starting with '#!'."#
165-
),
166-
167162
Trivia(
168163
name: "Space",
169164
comment: #"A space ' ' character."#,

Sources/SwiftIDEUtils/SyntaxClassification.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ extension RawTokenKind {
193193
return .none
194194
case .semicolon:
195195
return .none
196+
case .shebang:
197+
return .none
196198
case .singleQuote:
197199
return .stringLiteral
198200
case .stringQuote:

Sources/SwiftParser/Lexer/Cursor.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,11 @@ extension Lexer.Cursor {
905905
case UInt8(ascii: "\\"): _ = self.advance(); return Lexer.Result(.backslash)
906906

907907
case UInt8(ascii: "#"):
908+
// Try lex shebang.
909+
if self.isAtStartOfFile, self.peek(at: 1) == UInt8(ascii: "!") {
910+
self.advanceToEndOfLine()
911+
return Lexer.Result(.shebang)
912+
}
908913
// Try lex a raw string literal.
909914
if let delimiterLength = self.advanceIfOpeningRawStringDelimiter() {
910915
return Lexer.Result(.rawStringPoundDelimiter, stateTransition: .push(newState: .afterRawStringDelimiter(delimiterLength: delimiterLength)))
@@ -1171,12 +1176,6 @@ extension Lexer.Cursor {
11711176
default:
11721177
break
11731178
}
1174-
case UInt8(ascii: "#"):
1175-
guard start.isAtStartOfFile, self.advance(if: { $0 == "!" }) else {
1176-
break
1177-
}
1178-
self.advanceToEndOfLine()
1179-
continue
11801179
case UInt8(ascii: "<"), UInt8(ascii: ">"):
11811180
if self.tryLexConflictMarker(start: start) {
11821181
error = LexingDiagnostic(.sourceConflictMarker, position: start)
@@ -1189,6 +1188,7 @@ extension Lexer.Cursor {
11891188
UInt8(ascii: "}"), UInt8(ascii: "]"), UInt8(ascii: ")"),
11901189
UInt8(ascii: "@"), UInt8(ascii: ","), UInt8(ascii: ";"),
11911190
UInt8(ascii: ":"), UInt8(ascii: "\\"), UInt8(ascii: "$"),
1191+
UInt8(ascii: "#"),
11921192

11931193
// Start of integer/hex/float literals.
11941194
UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"),

Sources/SwiftParser/Lexer/RegexLiteralLexer.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,10 @@ extension Lexer.Cursor {
625625
case .arrow, .ellipsis, .period, .atSign, .pound, .backtick, .backslash:
626626
return false
627627

628+
// Shebang does not sequence expressions.
629+
case .shebang:
630+
return false
631+
628632
case .keyword:
629633
// There are a handful of keywords that are expressions, handle them.
630634
// Otherwise, a regex literal can generally be parsed after a keyword.

Sources/SwiftParser/TokenPrecedence.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ enum TokenPrecedence: Comparable {
124124
.dollarIdentifier, .identifier,
125125
// '_' can occur in types to replace a type identifier
126126
.wildcard,
127-
// String segment, string interpolation anchor, pound, and regex pattern don't really fit anywhere else
128-
.pound, .stringSegment, .regexLiteralPattern:
127+
// String segment, string interpolation anchor, pound, shebang and regex pattern don't really fit anywhere else
128+
.pound, .stringSegment, .regexLiteralPattern, .shebang:
129129
self = .identifierLike
130130

131131
// MARK: Expr keyword

Sources/SwiftParser/TopLevel.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ extension Parser {
4040
/// ``Parser/parse(source:parseTransition:filenameForDiagnostics:languageVersion:enableBareSlashRegexLiteral:)-7tndx``
4141
/// API calls.
4242
mutating func parseSourceFile() -> RawSourceFileSyntax {
43+
let shebang = self.consume(if: .shebang)
4344
let items = self.parseTopLevelCodeBlockItems()
4445
let unexpectedBeforeEndOfFileToken = consumeRemainingTokens()
4546
let endOfFile = self.consume(if: .endOfFile)!
4647
return .init(
48+
shebang: shebang,
4749
statements: items,
4850
RawUnexpectedNodesSyntax(unexpectedBeforeEndOfFileToken, arena: self.arena),
4951
endOfFileToken: endOfFile,

Sources/SwiftParser/TriviaParser.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,6 @@ public struct TriviaParser {
8181
}
8282

8383
case UInt8(ascii: "#"):
84-
// "#!...": .shebang
85-
// NOTE: .shebang appears only if this trivia is at the start of the
86-
// file. We don't know if this trivia is at the start of the file, but
87-
// we believe that the lexer lexed it accordingly.
88-
if position == .leading && pieces.isEmpty && cursor.advance(if: { $0 == "!" }) {
89-
cursor.advanceToEndOfLine()
90-
pieces.append(.shebang(start.text(upTo: cursor)))
91-
continue
92-
}
9384
cursor.advance(while: { $0 == "#" })
9485
pieces.append(.pounds(start.distance(to: cursor)))
9586
continue

Sources/SwiftParser/generated/TokenSpecStaticMembers.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ extension TokenSpec {
187187
return TokenSpec(.semicolon)
188188
}
189189

190+
static var shebang: TokenSpec {
191+
return TokenSpec(.shebang)
192+
}
193+
190194
static var singleQuote: TokenSpec {
191195
return TokenSpec(.singleQuote)
192196
}

0 commit comments

Comments
 (0)