Skip to content

Commit 42c1cef

Browse files
authored
Merge pull request #2605 from kimdv/kimdv/405-sr-13584-make-convenience-methods-returning-an-the-value-of-an-integer-literal-as-int-and-of-floating-literal-as-float-return-an-optional
Add `integerValue` to `IntegerLiteralExprSyntax` and `floatingValue` to `FloatLiteralExprSyntax`
2 parents 98c5144 + a576b13 commit 42c1cef

File tree

5 files changed

+149
-41
lines changed

5 files changed

+149
-41
lines changed

Release Notes/510.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
- `SyntaxStringInterpolation.appendInterpolation(_: (some SyntaxProtocol)?)`
66
- Description: Allows optional syntax nodes to be used inside string interpolation of syntax nodes. If the node is `nil`, nothing will get added to the string interpolation.
77
- Pull Request: https://github.com/apple/swift-syntax/pull/2085
8+
89
- `SyntaxCollection.index(at:)`
910
- Description: Returns the index of the n-th element in a `SyntaxCollection`. This computation is in O(n) and `SyntaxCollection` is not subscriptable by an integer.
1011
- Pull Request: https://github.com/apple/swift-syntax/pull/2014
12+
1113
- Convenience initializer `ClosureCaptureSyntax.init()`
1214
- Description: Provides a convenience initializer for `ClosureCaptureSyntax` that takes a concrete `name` argument and automatically adds `equal = TokenSyntax.equalToken()` to it.
1315
- Issue: https://github.com/apple/swift-syntax/issues/1984
1416
- Pull Request: https://github.com/apple/swift-syntax/pull/2127
17+
1518
- Convenience initializer `EnumCaseParameterSyntax.init()`
1619
- Description: Provides a convenience initializer for `EnumCaseParameterSyntax` that takes a concrete `firstName` value and adds `colon = TokenSyntax.colonToken()` automatically to it.
1720
- Issue: https://github.com/apple/swift-syntax/issues/1984
@@ -30,7 +33,7 @@
3033
- Issue: https://github.com/apple/swift-syntax/issues/2092
3134
- Pull Request: https://github.com/apple/swift-syntax/pull/2108
3235

33-
- Same-Type Casts
36+
- Same-Type Casts
3437
- Description: `is`, `as`, and `cast` overloads on `SyntaxProtocol` with same-type conversions are marked as deprecated. The deprecated methods emit a warning indicating the cast will always succeed.
3538
- Issue: https://github.com/apple/swift-syntax/issues/2092
3639
- Pull Request: https://github.com/apple/swift-syntax/pull/2108

Release Notes/601.md

+10
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@
22

33
## New APIs
44

5+
- `IntegerLiteralExprSyntax` and `FloatLiteralExprSyntax` now have a computed `representedLiteralValue` property.
6+
- Description: Allows retrieving the represented literal value when valid.
7+
- Issue: https://github.com/apple/swift-syntax/issues/405
8+
- Pull Request: https://github.com/apple/swift-syntax/pull/2605
9+
510
## API Behavior Changes
611

712
## Deprecations
813

914
## API-Incompatible Changes
1015

16+
- Moved `Radix` and `IntegerLiteralExprSyntax.radix` from `SwiftRefactor` to `SwiftSyntax`.
17+
- Description: Allows retrieving the radix value from the `literal.text`.
18+
- Issue: https://github.com/apple/swift-syntax/issues/405
19+
- Pull Request: https://github.com/apple/swift-syntax/pull/2605
20+
1121
## Template
1222

1323
- *Affected API or two word description*

Sources/SwiftRefactor/IntegerLiteralUtilities.swift

-40
Original file line numberDiff line numberDiff line change
@@ -17,46 +17,6 @@ import SwiftSyntax
1717
#endif
1818

1919
extension IntegerLiteralExprSyntax {
20-
public enum Radix {
21-
case binary
22-
case octal
23-
case decimal
24-
case hex
25-
26-
public var size: Int {
27-
switch self {
28-
case .binary: return 2
29-
case .octal: return 8
30-
case .decimal: return 10
31-
case .hex: return 16
32-
}
33-
}
34-
35-
/// The prefix that is used to express an integer literal with this
36-
/// radix in Swift source code, e.g., "0x" for hexadecimal.
37-
public var literalPrefix: String {
38-
switch self {
39-
case .binary: return "0b"
40-
case .octal: return "0o"
41-
case .hex: return "0x"
42-
case .decimal: return ""
43-
}
44-
}
45-
}
46-
47-
public var radix: Radix {
48-
let text = self.literal.text
49-
if text.starts(with: "0b") {
50-
return .binary
51-
} else if text.starts(with: "0o") {
52-
return .octal
53-
} else if text.starts(with: "0x") {
54-
return .hex
55-
} else {
56-
return .decimal
57-
}
58-
}
59-
6020
/// Returns an (arbitrarily) "ideal" number of digits that should constitute
6121
/// a separator-delimited "group" in an integer literal.
6222
var idealGroupSize: Int {

Sources/SwiftSyntax/Convenience.swift

+84
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,90 @@ extension EnumCaseParameterSyntax {
6868
}
6969
}
7070

71+
extension FloatLiteralExprSyntax {
72+
/// A computed property representing the floating-point value parsed from the associated `literal.text` property.
73+
///
74+
/// - Returns: A double value parsed from the associated`literal.text`, or `nil` if the text cannot be parsed as a double.
75+
public var representedLiteralValue: Double? {
76+
guard !hasError else { return nil }
77+
78+
let floatingDigitsWithoutUnderscores = literal.text.filter {
79+
$0 != "_"
80+
}
81+
82+
return Double(floatingDigitsWithoutUnderscores)
83+
}
84+
}
85+
86+
extension IntegerLiteralExprSyntax {
87+
public enum Radix {
88+
case binary
89+
case octal
90+
case decimal
91+
case hex
92+
93+
public var size: Int {
94+
switch self {
95+
case .binary: return 2
96+
case .octal: return 8
97+
case .decimal: return 10
98+
case .hex: return 16
99+
}
100+
}
101+
102+
/// The prefix that is used to express an integer literal with this
103+
/// radix in Swift source code, e.g., "0x" for hexadecimal.
104+
public var literalPrefix: String {
105+
switch self {
106+
case .binary: return "0b"
107+
case .octal: return "0o"
108+
case .hex: return "0x"
109+
case .decimal: return ""
110+
}
111+
}
112+
113+
fileprivate var offset: Int {
114+
switch self {
115+
case .binary, .hex, .octal:
116+
return 2
117+
case .decimal:
118+
return 0
119+
}
120+
}
121+
}
122+
123+
public var radix: Radix {
124+
let text = self.literal.text
125+
if text.starts(with: "0b") {
126+
return .binary
127+
} else if text.starts(with: "0o") {
128+
return .octal
129+
} else if text.starts(with: "0x") {
130+
return .hex
131+
} else {
132+
return .decimal
133+
}
134+
}
135+
136+
/// A computed property representing the integer value parsed from the associated `literal.text` property, considering the specified radix.
137+
///
138+
/// - Returns: An integer value parsed from the associated `literal.text`, or `nil` if the text cannot be parsed as an integer.
139+
public var representedLiteralValue: Int? {
140+
guard !hasError else { return nil }
141+
142+
let text = literal.text
143+
let radix = self.radix
144+
let digitsStartIndex = text.index(text.startIndex, offsetBy: radix.offset)
145+
let textWithoutPrefix = text.suffix(from: digitsStartIndex)
146+
147+
let textWithoutPrefixOrUnderscores = textWithoutPrefix.filter {
148+
$0 != "_"
149+
}
150+
151+
return Int(textWithoutPrefixOrUnderscores, radix: radix.size)
152+
}
153+
}
154+
71155
extension MemberAccessExprSyntax {
72156
/// Creates a new ``MemberAccessExprSyntax`` where the accessed member is represented by
73157
/// an identifier without specifying argument labels.

Tests/SwiftSyntaxTest/SyntaxTests.swift

+51
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,55 @@ class SyntaxTests: XCTestCase {
168168
XCTAssertEqual(funcKeywordInTree2?.as(TokenSyntax.self)?.tokenKind, .keyword(.func))
169169
XCTAssertNotEqual(funcKeywordInTree1.id, funcKeywordInTree2?.id)
170170
}
171+
172+
func testIntegerLiteralExprSyntax() {
173+
let testCases: [UInt: (String, Int?)] = [
174+
#line: ("2", 2),
175+
#line: ("02", 2),
176+
#line: ("020", 20),
177+
#line: ("-02", -2),
178+
#line: ("2_00_0000", 2_00_0000),
179+
#line: ("-2_00_0000", -2_00_0000),
180+
#line: ("foo", nil),
181+
#line: ("999999999999999999999999999999999999999999999999999999999999999999999999999999", nil),
182+
#line: ("0b1010101", 85),
183+
#line: ("0xFF", 255),
184+
#line: ("0o777", 511),
185+
#line: ("0b001100", 0b001100),
186+
#line: ("4_2", 4_2),
187+
#line: ("0o3434", 0o3434),
188+
#line: ("0xba11aD", 0xba11aD),
189+
#line: ("🐋", nil),
190+
#line: ("-0xA", nil),
191+
#line: ("-0o7", nil),
192+
#line: ("-0b1", nil),
193+
]
194+
195+
for (line, testCase) in testCases {
196+
let (value, expected) = testCase
197+
let expr = IntegerLiteralExprSyntax(literal: .integerLiteral(value))
198+
XCTAssertEqual(expr.representedLiteralValue, expected, line: line)
199+
}
200+
}
201+
202+
func testFloatLiteralExprSyntax() {
203+
let testCases: [UInt: (String, Double?)] = [
204+
#line: ("2", 2),
205+
#line: ("2_00_00.001", 2_00_00.001),
206+
#line: ("5.3_8", 5.3_8),
207+
#line: ("12e3", 12000.0),
208+
#line: ("32E1", 320.0),
209+
#line: ("0xdEFACE.C0FFEEp+1", 0xdEFACE.C0FFEEp+1),
210+
#line: ("0xaffab1e.e1fP-2", 0xaffab1e.e1fP-2),
211+
#line: ("🥥", nil),
212+
#line: ("12e+3", 12000.0),
213+
#line: ("12e-3", 0.012),
214+
]
215+
216+
for (line, testCase) in testCases {
217+
let (value, expected) = testCase
218+
let expr = FloatLiteralExprSyntax(literal: .floatLiteral(value))
219+
XCTAssertEqual(expr.representedLiteralValue, expected, line: line)
220+
}
221+
}
171222
}

0 commit comments

Comments
 (0)