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

Implement generic fromBin|Oct|Hex in strutils #11107

Merged
merged 12 commits into from
May 23, 2019
Merged

Implement generic fromBin|Oct|Hex in strutils #11107

merged 12 commits into from
May 23, 2019

Conversation

avitkauskas
Copy link
Contributor

@avitkauskas avitkauskas commented Apr 24, 2019

Implement all families of proc like parseBinInt8, parseBinInt16, ..., parseBinUint64 (and Oct|Hex) in strutils returning their respective types of integer.

@kaushalmodi
Copy link
Contributor

Is it possible to design generics for this? I don't like the idea of hardcoding types (int, uint, etc) in proc names.

@kaushalmodi
Copy link
Contributor

kaushalmodi commented Apr 25, 2019

There was an awesome effort put in by @timotheecour a while back: #10346, but unfortunately they PR got closed.

See nim-lang/RFCs#84.

@avitkauskas
Copy link
Contributor Author

avitkauskas commented Apr 25, 2019

@kaushalmodi I think this could be easily done by:

import parseutils

proc parseFromBin*[T: SomeInteger](v: var T, s: string) =
     let p = parseutils.parseBin(s, v)
     if p != s.len or p == 0:
       raise newException(ValueError, "invalid binary integer: " & s)

var bin: int8
bin.parseFromBin("0b11011")

doAssert bin == 0b11011

... or something. But there is already the proc parseBinInt in stdlib, so I wanted just to expand this to other types. Actually parsutils.parseBin is now generic and does almost the same, but without the Exception guard.

@kaushalmodi
Copy link
Contributor

But there is already the proc parseBinInt in stdlib

Then may be that should be deprecated? Asking @Araq to weigh in on this.

@avitkauskas
Copy link
Contributor Author

avitkauskas commented Apr 25, 2019

Mistery - koch changes oct literal when generating doc examples!

my code in strutils.nim:

runnableExamples:
    let a = "0o_123_456_777"
    doAssert a.parseOctInt8() == 0o377'i8

./koch doc generates in example file:

block:
  let a = "0o_123_456_777"
  doAssert a.parseOctInt8() == 0o777'i8  # <--- see this!

and fails with the error:

strutils_examples29.nim(9, 32) Error: number out of range: '0o777'i8'

If I try 0o277'i8 it fails with 0o677'i8.
Is that my problem, or koch?

@mratsim
Copy link
Collaborator

mratsim commented Apr 26, 2019

0o777 is 0x1FF in hexadecimal, a byte can represent from 0x00 to 0xFF. And 0xFF is 0o377 so the output is correct modulo the int8 range.

@avitkauskas
Copy link
Contributor Author

avitkauskas commented Apr 26, 2019

The thing is - I put in runableExamples 0o377'i8 (and that's 0xFF, correct), but koch doc when generating examples changes this to 0o777'i8 in the generated examples file, and then of course tests fail with the error message Error: number out of range: '0o777'i8'.

But how can it be that this change happens while extracting my code into separate examples file?
All other lines arround this one are transfered to example file verbatim without any changes.

@Araq
Copy link
Member

Araq commented Apr 29, 2019

But how can it be that this change happens while extracting my code into separate examples file?

Well the "extraction" is ... er... special. :-)

@data-man
Copy link
Contributor

parseXIntX in the docs, but parseXintX by fact.
Isn't good especially for autocompletion.

@avitkauskas
Copy link
Contributor Author

avitkauskas commented Apr 29, 2019

@data-man Yes, you are right, there would be a problem for autocompletion as you say.
So, maybe it would be much easier and better then to introduce procs like:

proc fromBin*[T: SomeInteger](v: var T, s: string) =
     let p = parseutils.parseBin(s, v)
     if p != s.len or p == 0:
       raise newException(ValueError, "invalid binary integer: " & s)

var x: int8
x.fromBin("0b11011")

Especially when there are already procs toBin etc. in strutils.

And then we could probably deprecate parseBinInt if we get the agreement on that.

@data-man
Copy link
Contributor

Personally I'd prefer universal parseInt with base parameter. And deprecate others.
What if someone wants to add a parseTernaryInt and family tomorrow? Or/and parseSeptenaryInt and family?
strutils is already big enough.

@mratsim
Copy link
Collaborator

mratsim commented Apr 30, 2019

They add this parseTernaryInt in their own library just like I did here: https://github.com/status-im/nim-stint/blob/6853ebe97c21426952ce64c2df6278514797dc3f/stint/io.nim#L116-L117

func parse*[bits: static[int]](input: string, T: typedesc[Stuint[bits]], radix: static[uint8] = 10): T =
  ## Parse a string and store the result in a Stuint[bits].

func parse*[bits: static[int]](input: string, T: typedesc[Stint[bits]], radix: static[int8] = 10): T =
  ## Parse a string and store the result in a Stint[bits] or Stuint[bits].

@@ -1068,44 +1068,145 @@ proc parseFloat*(s: string): float {.noSideEffect, procvar,

proc parseBinInt*(s: string): int {.noSideEffect, procvar,
rtl, extern: "nsuParseBinInt".} =
## Parses a binary integer value contained in `s`.
## Parses a binary integer value contained in `s` and returns `int`.
Copy link
Member

Choose a reason for hiding this comment

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

"And returns int"? Do we need to assume nobody can read Nim type declarations?

@Araq
Copy link
Member

Araq commented May 2, 2019

I appreciate the effort but this should use generics indeed. You can then deprecate the non-generic variant or simply leave it as it is.

@avitkauskas
Copy link
Contributor Author

avitkauskas commented May 5, 2019

@Araq When talking about generics, what variant would you prefer:

proc fromBin*[T: SomeInteger](x: var T, s: string) =
  let p = parseutils.parseBin(s, x)
  if p != s.len or p == 0:
    raise newException(ValueError, "invalid binary integer: " & s)

var x: int8
x.fromBin("0b11011")

or

proc fromBin*[T: SomeInteger](s: string): T =
  let p = parseutils.parseBin(s, result)
  if p != s.len or p == 0:
    raise newException(ValueError, "invalid binary integer: " & s)

var x: int8
x = fromBin[int8]("0b11011")

# by the way, x = "0b11011".fromBin[int8] would not work: 
# cannot instantiate: 'T' and other errors

@Araq
Copy link
Member

Araq commented May 8, 2019

This variant:

proc fromBin*[T: SomeInteger](s: string): T =
  let p = parseutils.parseBin(s, result)
  if p != s.len or p == 0:
    raise newException(ValueError, "invalid binary integer: " & s)

var x: int8
x = fromBin[int8]("0b11011")

# by the way, x = "0b11011".fromBin[:int8] would work perfectly fine.

@mratsim
Copy link
Collaborator

mratsim commented May 9, 2019

I feel like we're back to typedesc vs generics API design here with a sprinkle of backward compatibility concerns (nim-lang/RFCs#40)

Anyway, I would be happy to merge the non-controversial state from here: c1d7ea9 and discuss/split the later commits with API issues in a separate PR.

@Araq
Copy link
Member

Araq commented May 9, 2019

I feel like we're back to typedesc vs generics API design here with a sprinkle of backward compatibility concerns (nim-lang/RFCs#40)

The stdlib uses [T] and so that's what should be used. I can find an RFC for everything that could be used to delay further progress.

@avitkauskas
Copy link
Contributor Author

runnableExamples tests will not pass until #11131 is fixed.

@avitkauskas avitkauskas changed the title Implement families of parse(Bin|Oct|Hex)Int[xx] in strutils Implement generic fromBin|Oct|Hex in strutils May 12, 2019
@avitkauskas
Copy link
Contributor Author

avitkauskas commented May 12, 2019

What should I do about that?

Hint: test_vectors [Processing]
test_vectors.nim(142, 27) Error: ambiguous call; both utils.fromHex(a: string) [declared in ..\..\..\..\..\Users\appveyor\.nimble\pkgs\nimcrypto-0.3.9\nimcrypto\utils.nim(158, 6)] and strutils.fromHex(s: string) [declared in ..\..\..\lib\pure\strutils.nim(1059, 6)] match for: (string)

@narimiran
Copy link
Member

runnableExamples tests will not pass until #11131 is fixed.

This is fixed now.

What should I do about that?

Where does test_vectors.nim come from? I didn't find it in Nim repo, is it some Nimble package?

@avitkauskas
Copy link
Contributor Author

@narimiran, it's in nimcrypto-0.3.9 package as I understand.

@narimiran
Copy link
Member

it's in nimcrypto-0.3.9 package as I understand.

Ah, I found it. It is blscurve package that is failing, and here is test_vectors.nim

test_vectors.nim(142, 27) Error: ambiguous call; both utils.fromHex(a: string) [declared in nimcrypto\utils.nim(158, 6)] and strutils.fromHex(s: string) [declared in lib\pure\strutils.nim(1059, 6)] match for: (string)

This can then be fixed by patching the mentioned package by having this line:

import strutils except fromHex

@mratsim
Copy link
Collaborator

mratsim commented May 18, 2019

Merged status-im/nim-blscurve#21 and retriggering CI. Can we put Appveyor under a Nim organisation. I can restart Travis builds but not Appveyor's.

@sophisticate364
Copy link

sophisticate364 commented May 18, 2019 via email

@avitkauskas
Copy link
Contributor Author

This is ready for review.

@Araq Araq merged commit 981f957 into nim-lang:devel May 23, 2019
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.

7 participants