Skip to content

Commit 11ec8d5

Browse files
committed
Apply feedback from review.
1 parent fc54a05 commit 11ec8d5

File tree

3 files changed

+113
-109
lines changed

3 files changed

+113
-109
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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+
@_spi(RawSyntax) import SwiftSyntax
14+
15+
extension StringLiteralExprSyntax {
16+
17+
/// Returns the string value of the literal as the parsed program would see
18+
/// it: Multiline strings are combined into one string, escape sequences are
19+
/// resolved.
20+
///
21+
/// Returns nil if the literal contains interpolation segments.
22+
23+
public var contentValue: String? {
24+
// Currently the implementation relies on properly parsed literals.
25+
guard !hasError else { return nil }
26+
guard let stringLiteralKind else { return nil }
27+
28+
// Concatenate unescaped string literal segments. For example multiline
29+
// strings consist of multiple segments. Abort on finding string
30+
// interpolation.
31+
var result = ""
32+
for segment in segments {
33+
switch segment {
34+
case .stringSegment(let stringSegmentSyntax):
35+
stringSegmentSyntax.appendUnescapedLiteralValue(
36+
stringLiteralKind: stringLiteralKind,
37+
delimiterLength: delimiterLength,
38+
to: &result
39+
)
40+
case .expressionSegment:
41+
// Bail out if there are any interpolation segments.
42+
return nil
43+
}
44+
}
45+
46+
return result
47+
}
48+
49+
fileprivate var stringLiteralKind: StringLiteralKind? {
50+
switch openQuote.tokenKind {
51+
case .stringQuote:
52+
return .singleLine
53+
case .multilineStringQuote:
54+
return .multiLine
55+
case .singleQuote:
56+
return .singleQuote
57+
default:
58+
return nil
59+
}
60+
}
61+
62+
fileprivate var delimiterLength: Int {
63+
openDelimiter?.text.count ?? 0
64+
}
65+
}
66+
67+
extension StringSegmentSyntax {
68+
fileprivate func appendUnescapedLiteralValue(
69+
stringLiteralKind: StringLiteralKind,
70+
delimiterLength: Int,
71+
to output: inout String
72+
) {
73+
precondition(!hasError, "appendUnescapedLiteralValue relies on properly parsed literals")
74+
75+
var text = content.text
76+
text.withUTF8 { buffer in
77+
var cursor = Lexer.Cursor(input: buffer, previous: 0)
78+
79+
// Put the cursor in the string literal lexing state. This is just
80+
// defensive as it's currently not used by `lexCharacterInStringLiteral`.
81+
let state = Lexer.Cursor.State.inStringLiteral(kind: stringLiteralKind, delimiterLength: delimiterLength)
82+
let transition = Lexer.StateTransition.push(newState: state)
83+
cursor.perform(stateTransition: transition, stateAllocator: BumpPtrAllocator(slabSize: 256))
84+
85+
while true {
86+
let lex = cursor.lexCharacterInStringLiteral(
87+
stringLiteralKind: stringLiteralKind,
88+
delimiterLength: delimiterLength
89+
)
90+
91+
switch lex {
92+
case .success(let scalar):
93+
output.append(Character(scalar))
94+
case .validatedEscapeSequence(let character):
95+
output.append(character)
96+
case .endOfString, .error:
97+
// We get an error at the end of the string because
98+
// `lexCharacterInStringLiteral` expects the closing quote.
99+
// We can assume the error just signals the end of string
100+
// because we made sure the token lexed fine before.
101+
return
102+
}
103+
}
104+
}
105+
}
106+
}

Sources/SwiftParser/StringLiterals.swift

Lines changed: 0 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -598,105 +598,3 @@ public extension RawTriviaPiece {
598598
}
599599
}
600600
}
601-
602-
// MARK: - StringLiteralExprSyntax/contentValue
603-
604-
extension StringLiteralExprSyntax {
605-
606-
/// Returns the string value of the literal as the parsed program would see
607-
/// it: Multiline strings are combined into one string, escape sequences are
608-
/// resolved.
609-
///
610-
/// Returns nil if the literal contains interpolation segments.
611-
612-
public var contentValue: String? {
613-
// Currently the implementation relies on properly parsed literals.
614-
guard !hasError else { return nil }
615-
616-
// Concatenate unescaped string literal segments. For example multiline
617-
// strings consist of multiple segments. Abort on finding string
618-
// interpolation.
619-
var result = ""
620-
for segment in segments {
621-
switch segment {
622-
case .stringSegment(let stringSegmentSyntax):
623-
stringSegmentSyntax.appendUnescapedLiteralValue(
624-
stringLiteralKind: stringLiteralKind,
625-
delimiterLength: delimiterLength,
626-
to: &result
627-
)
628-
case .expressionSegment:
629-
// Bail out if there are any interpolation segments.
630-
return nil
631-
}
632-
}
633-
634-
return result
635-
}
636-
637-
fileprivate var stringLiteralKind: StringLiteralKind {
638-
switch openQuote.tokenKind {
639-
case .stringQuote:
640-
return .singleLine
641-
case .multilineStringQuote:
642-
return .multiLine
643-
case .singleQuote:
644-
return .singleQuote
645-
default:
646-
fatalError()
647-
}
648-
}
649-
650-
fileprivate var delimiterLength: Int {
651-
openDelimiter?.text.count ?? 0
652-
}
653-
}
654-
655-
extension StringSegmentSyntax {
656-
fileprivate func appendUnescapedLiteralValue(
657-
stringLiteralKind: StringLiteralKind,
658-
delimiterLength: Int,
659-
to output: inout String
660-
) {
661-
precondition(!hasError, "impl. relies on properly parsed literals")
662-
663-
var text = content.text
664-
665-
// Fast path for literals without escape sequences.
666-
// TODO: Check if this isn't actually slower.
667-
guard text.contains("\\") else {
668-
output.append(text)
669-
return
670-
}
671-
672-
text.withUTF8 { buffer in
673-
var cursor = Lexer.Cursor(input: buffer, previous: 0)
674-
675-
// Put the cursor in the string literal lexing state. This is just
676-
// defensive as it's currently not used by `lexCharacterInStringLiteral`.
677-
let state = Lexer.Cursor.State.inStringLiteral(kind: stringLiteralKind, delimiterLength: delimiterLength)
678-
let transition = Lexer.StateTransition.push(newState: state)
679-
cursor.perform(stateTransition: transition, stateAllocator: BumpPtrAllocator(slabSize: 256))
680-
681-
while true {
682-
let lex = cursor.lexCharacterInStringLiteral(
683-
stringLiteralKind: stringLiteralKind,
684-
delimiterLength: delimiterLength
685-
)
686-
687-
switch lex {
688-
case .success(let scalar):
689-
output.append(Character(scalar))
690-
case .validatedEscapeSequence(let character):
691-
output.append(character)
692-
case .endOfString, .error:
693-
// We get an error at the end of the string because
694-
// `lexCharacterInStringLiteral` expects the closing quote.
695-
// We can assume the error just signals the end of string
696-
// because we made sure the token lexed fine before.
697-
return
698-
}
699-
}
700-
}
701-
}
702-
}

Tests/SwiftParserTest/StringLiteralContentValueTests.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class StringLiteralContentValueTests: XCTestCase {
2929
contentValue.`` It does so by comparing the run-time String value of various
3030
string literals to the value returned from `contentValue`.
3131
32-
The Swift-Syntax parsed representation `StringLiteralExprSyntax` contains
32+
The SwiftSyntax parsed representation `StringLiteralExprSyntax` contains
3333
the source-accurate representation of the string literal. This
3434
representation differs from the runtime value:
3535
@@ -250,18 +250,18 @@ public class StringLiteralContentValueTests: XCTestCase {
250250
// MARK: literal value not available
251251

252252
func testMissingQuoteStringLiteral() throws {
253-
let sut = StringLiteralExprSyntax(#""a"# as ExprSyntax)!
254-
XCTAssertNil(sut.contentValue, "only fully parsed string literals should produce a literal value")
253+
let stringLiteral = StringLiteralExprSyntax(#""a"# as ExprSyntax)!
254+
XCTAssertNil(stringLiteral.contentValue, "only fully parsed string literals should produce a literal value")
255255
}
256256

257257
func testInterpolatedStringLiteral() throws {
258-
let sut = StringLiteralExprSyntax(#""abc\(1)""# as ExprSyntax)!
259-
XCTAssertNil(sut.contentValue, "interpolated string literals cannot produce a literal value")
258+
let stringLiteral = StringLiteralExprSyntax(#""abc\(1)""# as ExprSyntax)!
259+
XCTAssertNil(stringLiteral.contentValue, "interpolated string literals cannot produce a literal value")
260260
}
261261

262262
func testMalformedMultiLineStringLiteral() throws {
263-
let sut = StringLiteralExprSyntax(#""""a""""# as ExprSyntax)!
264-
XCTAssertNil(sut.contentValue, "missing newline in multiline string literal cannot produce a literal value")
263+
let stringLiteral = StringLiteralExprSyntax(#""""a""""# as ExprSyntax)!
264+
XCTAssertNil(stringLiteral.contentValue, "missing newline in multiline string literal cannot produce a literal value")
265265
}
266266

267267
// MARK: supporting code

0 commit comments

Comments
 (0)