Skip to content

Add integerValue to IntegerLiteralExprSyntax and floatingValue to FloatLiteralExprSyntax #2605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Release Notes/510.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
- `SyntaxStringInterpolation.appendInterpolation(_: (some SyntaxProtocol)?)`
- 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.
- Pull Request: https://github.com/apple/swift-syntax/pull/2085

- `SyntaxCollection.index(at:)`
- 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.
- Pull Request: https://github.com/apple/swift-syntax/pull/2014

- Convenience initializer `ClosureCaptureSyntax.init()`
- Description: Provides a convenience initializer for `ClosureCaptureSyntax` that takes a concrete `name` argument and automatically adds `equal = TokenSyntax.equalToken()` to it.
- Issue: https://github.com/apple/swift-syntax/issues/1984
- Pull Request: https://github.com/apple/swift-syntax/pull/2127

- Convenience initializer `EnumCaseParameterSyntax.init()`
- Description: Provides a convenience initializer for `EnumCaseParameterSyntax` that takes a concrete `firstName` value and adds `colon = TokenSyntax.colonToken()` automatically to it.
- Issue: https://github.com/apple/swift-syntax/issues/1984
Expand All @@ -30,7 +33,7 @@
- Issue: https://github.com/apple/swift-syntax/issues/2092
- Pull Request: https://github.com/apple/swift-syntax/pull/2108

- Same-Type Casts
- Same-Type Casts
- 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.
- Issue: https://github.com/apple/swift-syntax/issues/2092
- Pull Request: https://github.com/apple/swift-syntax/pull/2108
Expand Down
10 changes: 10 additions & 0 deletions Release Notes/601.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@

## New APIs

- `IntegerLiteralExprSyntax` and `FloatLiteralExprSyntax` now have a computed `representedLiteralValue` property.
- Description: Allows retrieving the represented literal value when valid.
- Issue: https://github.com/apple/swift-syntax/issues/405
- Pull Request: https://github.com/apple/swift-syntax/pull/2605

## API Behavior Changes

## Deprecations

## API-Incompatible Changes

- Moved `Radix` and `IntegerLiteralExprSyntax.radix` from `SwiftRefactor` to `SwiftSyntax`.
- Description: Allows retrieving the radix value from the `literal.text`.
- Issue: https://github.com/apple/swift-syntax/issues/405
- Pull Request: https://github.com/apple/swift-syntax/pull/2605

## Template

- *Affected API or two word description*
Expand Down
40 changes: 0 additions & 40 deletions Sources/SwiftRefactor/IntegerLiteralUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,6 @@ import SwiftSyntax
#endif

extension IntegerLiteralExprSyntax {
public enum Radix {
case binary
case octal
case decimal
case hex

public var size: Int {
switch self {
case .binary: return 2
case .octal: return 8
case .decimal: return 10
case .hex: return 16
}
}

/// The prefix that is used to express an integer literal with this
/// radix in Swift source code, e.g., "0x" for hexadecimal.
public var literalPrefix: String {
switch self {
case .binary: return "0b"
case .octal: return "0o"
case .hex: return "0x"
case .decimal: return ""
}
}
}

public var radix: Radix {
let text = self.literal.text
if text.starts(with: "0b") {
return .binary
} else if text.starts(with: "0o") {
return .octal
} else if text.starts(with: "0x") {
return .hex
} else {
return .decimal
}
}

/// Returns an (arbitrarily) "ideal" number of digits that should constitute
/// a separator-delimited "group" in an integer literal.
var idealGroupSize: Int {
Expand Down
84 changes: 84 additions & 0 deletions Sources/SwiftSyntax/Convenience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,90 @@ extension EnumCaseParameterSyntax {
}
}

extension FloatLiteralExprSyntax {
/// A computed property representing the floating-point value parsed from the associated `literal.text` property.
///
/// - Returns: A double value parsed from the associated`literal.text`, or `nil` if the text cannot be parsed as a double.
public var representedLiteralValue: Double? {
guard !hasError else { return nil }

let floatingDigitsWithoutUnderscores = literal.text.filter {
$0 != "_"
}

return Double(floatingDigitsWithoutUnderscores)
}
}

extension IntegerLiteralExprSyntax {
public enum Radix {
case binary
case octal
case decimal
case hex

public var size: Int {
switch self {
case .binary: return 2
case .octal: return 8
case .decimal: return 10
case .hex: return 16
}
}

/// The prefix that is used to express an integer literal with this
/// radix in Swift source code, e.g., "0x" for hexadecimal.
public var literalPrefix: String {
switch self {
case .binary: return "0b"
case .octal: return "0o"
case .hex: return "0x"
case .decimal: return ""
}
}

fileprivate var offset: Int {
switch self {
case .binary, .hex, .octal:
return 2
case .decimal:
return 0
}
}
}

public var radix: Radix {
let text = self.literal.text
if text.starts(with: "0b") {
return .binary
} else if text.starts(with: "0o") {
return .octal
} else if text.starts(with: "0x") {
return .hex
} else {
return .decimal
}
}

/// A computed property representing the integer value parsed from the associated `literal.text` property, considering the specified radix.
///
/// - Returns: An integer value parsed from the associated `literal.text`, or `nil` if the text cannot be parsed as an integer.
public var representedLiteralValue: Int? {
guard !hasError else { return nil }

let text = literal.text
let radix = self.radix
let digitsStartIndex = text.index(text.startIndex, offsetBy: radix.offset)
let textWithoutPrefix = text.suffix(from: digitsStartIndex)

let textWithoutPrefixOrUnderscores = textWithoutPrefix.filter {
$0 != "_"
}

return Int(textWithoutPrefixOrUnderscores, radix: radix.size)
}
}

extension MemberAccessExprSyntax {
/// Creates a new ``MemberAccessExprSyntax`` where the accessed member is represented by
/// an identifier without specifying argument labels.
Expand Down
51 changes: 51 additions & 0 deletions Tests/SwiftSyntaxTest/SyntaxTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,55 @@ class SyntaxTests: XCTestCase {
XCTAssertEqual(funcKeywordInTree2?.as(TokenSyntax.self)?.tokenKind, .keyword(.func))
XCTAssertNotEqual(funcKeywordInTree1.id, funcKeywordInTree2?.id)
}

func testIntegerLiteralExprSyntax() {
let testCases: [UInt: (String, Int?)] = [
#line: ("2", 2),
#line: ("02", 2),
#line: ("020", 20),
#line: ("-02", -2),
#line: ("2_00_0000", 2_00_0000),
#line: ("-2_00_0000", -2_00_0000),
#line: ("foo", nil),
#line: ("999999999999999999999999999999999999999999999999999999999999999999999999999999", nil),
#line: ("0b1010101", 85),
#line: ("0xFF", 255),
#line: ("0o777", 511),
#line: ("0b001100", 0b001100),
#line: ("4_2", 4_2),
#line: ("0o3434", 0o3434),
#line: ("0xba11aD", 0xba11aD),
#line: ("🐋", nil),
#line: ("-0xA", nil),
#line: ("-0o7", nil),
#line: ("-0b1", nil),
]

for (line, testCase) in testCases {
let (value, expected) = testCase
let expr = IntegerLiteralExprSyntax(literal: .integerLiteral(value))
XCTAssertEqual(expr.representedLiteralValue, expected, line: line)
}
}

func testFloatLiteralExprSyntax() {
let testCases: [UInt: (String, Double?)] = [
#line: ("2", 2),
#line: ("2_00_00.001", 2_00_00.001),
#line: ("5.3_8", 5.3_8),
#line: ("12e3", 12000.0),
#line: ("32E1", 320.0),
#line: ("0xdEFACE.C0FFEEp+1", 0xdEFACE.C0FFEEp+1),
#line: ("0xaffab1e.e1fP-2", 0xaffab1e.e1fP-2),
#line: ("🥥", nil),
#line: ("12e+3", 12000.0),
#line: ("12e-3", 0.012),
]

for (line, testCase) in testCases {
let (value, expected) = testCase
let expr = FloatLiteralExprSyntax(literal: .floatLiteral(value))
XCTAssertEqual(expr.representedLiteralValue, expected, line: line)
}
}
}