Skip to content
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

Allow parsing negative numbers #188

Closed
wants to merge 2 commits into from

Conversation

john-mueller
Copy link
Contributor

Allows negative numbers to be passed as options and arguments.

Fixes #31

Checklist

  • I've added at least one test that validates that my change is working, if appropriate
  • I've followed the code style of the rest of the project
  • I've read the Contribution Guidelines
  • I've updated the documentation if necessary (no need)

Copy link
Member

@natecook1000 natecook1000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR, @john-mueller! Unfortunately this approach isn't totally workable, since you can define @Option(name: .customShort("2")) var foo: Int. It doesn't look like we have a test case for this, but we can't treat all negative numerals as numeric values. We need to be able to treat them as values only when they're in a position to be interpreted as such.

For a different approach, you could look at adding something like static var hasDashPrefixedRepresentations: Bool to ExpressibleByArgument, then customizing that for the signed numeric types, and then plumbing it through the parsing to pick up single-dash prefixed values when appropriate. What do you think?

@john-mueller
Copy link
Contributor Author

Hmm, good point. I wasn't thinking about ip -6, for example. Let me take another crack at it.

Added case SplitArguments.Element.possibleNegative to delay choosing between a value and an option until more context is known.

Fixed bug when an Option taking an array of values used ArrayParsingStrategy.upToNextOption and Name.longWithSingleDash.
@john-mueller
Copy link
Contributor Author

Okay, this should be a fully robust solution. I added a .possibleNegative case to SplitArguments.Element. This preserves the potential value and option until enough context is known to determine which it is.

I also found and fixed a bug when using ArrayParsingStrategy.upToNextOption and Name.longWithSingleDash together. Those tests are added to LongNameWithShortDashEndToEndTests.swift.

Copy link
Member

@natecook1000 natecook1000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your hard work on this, @john-mueller! Your tests look very comprehensive, especially the coverage in NegativeNumbersEndToEndTests.swift. As you’ve seen, there’s quite a bit of subtlety and interaction when parsing option groups, and it looks like you’re exploring that complexity quite well.

I’d still like to constrain this expansion of value matching only to values where we know that a negative number is a valid representation — i.e. floating point and signed integer types. Allowing values like -5 to match for strings or other types doesn’t quite line up with the norms for command-line tools and the different parsing strategies that are already defined in the library. Since you’re parsing (possible) negative values correctly now, the next step would be to look at only actually using these values for specific types that conform to ExpressibleByArgument.

@@ -142,6 +142,19 @@ extension ArgumentDefinition: CustomDebugStringConvertible {
}
}

extension ArgumentDefinition: Equatable {
static func == (lhs: ArgumentDefinition, rhs: ArgumentDefinition) -> Bool {
lhs.names == rhs.names && lhs.parsingStrategy == rhs.parsingStrategy
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add kind and help to this list as well? We can't test true equality b/c of the closures, but this implementation will return true for any two positional arguments with the same parsing strategy.

@@ -331,6 +333,28 @@ extension ArgumentSet {
case .value:
// We'll parse positional values later.
break
case let .possibleNegative(_, parsed):
// If this matches a short name or long name with single dash,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section is a bit tricky — can you add examples of what we're dealing with here? e.g. in this first section, with a call of example -5 -5, the first -5 could match a flag and fall through, and then the second -5 would be treated as a potential positional argument. For the second, I guess it's something like example -56, where there are -5 and a -6 flags, neither of which have been used yet?

func hash(into hasher: inout Hasher) {
hasher.combine(names)
hasher.combine(parsingStrategy)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include kind and help here, as well.

@@ -445,21 +467,22 @@ extension SplitArguments {
case 0:
append(.value(arg))
case 1:
let possibleNegativeNumber = Int(arg) != nil || Double(arg) != nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let possibleNegativeNumber = Int(arg) != nil || Double(arg) != nil
let possibleNegativeNumber = Double(arg) != nil

Double can handle the full range of negative Ints, so just that check should be fine here.

@@ -432,6 +444,16 @@ extension SplitArguments {
elements.append((index, element))
}

/// Append as `.possibleNegative` if it could be a negative value;
/// otherwise, append as `.option`.
func appendAsPossibleNegative(if isDashPrefixedNumber: Bool, value: String, option: ParsedArgument) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we name this appendOption(_:value:isPossibleNegative:)?

@shahmishal shahmishal closed this Oct 6, 2020
@shahmishal
Copy link
Member

The Swift project moved the default branch to main and deleted master branch, so GitHub automatically closed the PR. Please re-create pull request with main branch.

More detail about the branch update - https://forums.swift.org/t/updating-branch-names/40412

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support passing a negative number as an argument
3 participants