lcompilers / lpython

Python compiler
https://lpython.org/
Other
1.5k stars 158 forks source link

Implement `str` to `int` conversion #2599

Open kmr-srbh opened 6 months ago

kmr-srbh commented 6 months ago

Resolves #2554

Implement basic string to integer conversion using CharacterToInteger casting, which I believe is a part of LFortran, but was not implemented here in LPython.

Working

from lpython import i8, i16, i32, i64

int8: i8 = i8("100")
int16: i16 = i16("100")
int32: i32 = i32("100")
int64: i64 = i64("100")

print("int8:", int8)
print("int16:", int16)
print("int32:", int32)
print("int64:", int64)
print()
print("int8 + i8(10):", int8 + i8(10))
print("int16 * i16(2):", int16 * i16(2))
print("int32 - 50:", int32 - 50)
print("int64 / i64(2):", int64 / i64(2))
(base) saurabh-kumar@Awadh:~/Projects/System/lpython$ ./src/bin/lpython ./examples/example.py
int8: 100
int16: 100
int32: 100
int64: 100

int8 + i8(10): 110
int16 * i16(2): 200
int32 - 50: 50
int64 / i64(2): 5.00000000000000000e+01

ASR

                            int32:
                                (Variable
                                    2
                                    int32
                                    []
                                    Local
                                    (Cast
                                        (StringConstant
                                            "100"
                                            (Character 1 3 ())
                                        )
                                        CharacterToInteger
                                        (Integer 4)
                                        (IntegerConstant 100 (Integer 4))
                                    )
                                    (IntegerConstant 100 (Integer 4))
                                    Default
                                    (Integer 4)
                                    ()
                                    Source
                                    Public
                                    Required
                                    .false.
                                ),
                            int64:
                                (Variable
                                    2
                                    int64
                                    []
                                    Local
                                    (Cast
                                        (StringConstant
                                            "100"
                                            (Character 1 3 ())
                                        )
                                        CharacterToInteger
                                        (Integer 8)
                                        (IntegerConstant 100 (Integer 8))
                                    )
                                    (IntegerConstant 100 (Integer 8))
                                    Default
                                    (Integer 8)
                                    ()
                                    Source
                                    Public
                                    Required
                                    .false.
                                ),

Tasks

Shaikh-Ubaid commented 6 months ago

Please mark this as "Ready for review" when ready. If you have any doubt, please ask.

kmr-srbh commented 6 months ago

@Shaikh-Ubaid I do have some doubts. They are related to throwing errors.

  1. Where should the case of an invalid string literal passed to the numeric types be handled? Before coming to the casting part, we cannot be sure that the passed value is an integer enclosed inside a string. This can be dealt with before we call the cast_helper, but then again, we would be repeating the same code as that in the casting. Handling the same during casting is not clean in my view.
  2. This is very important. We will have to handle the case of an integer larger than the storage type passed to the variable. stoi and stol handle that for us for i32 and i64, but for the smaller ones it leads to the overflow.

My chief concern is that while both of these can be done while casting, it is not clean, and probably not supported there too.

Shaikh-Ubaid commented 6 months ago

Sharing my thoughts on this:

I believe the above approach solves all your concerns.

Shaikh-Ubaid commented 6 months ago

Also see https://github.com/lcompilers/lpython/issues/2554#issuecomment-2002459088.

kmr-srbh commented 6 months ago

Now for conversion to string, let's use the int() function

I think there is a catch. @certik said that int() will mean an arbitrary precision integer in future for LPython. Considering the case of a hexadecimal or an octal number, the result of something like i32(int("0x101100", 16))" will cause an error as the value is too large to fit in an i32.

Supporting your idea and building upon it, I think we need to not just cast a string to an integer, but convert it. Please let me know your thoughts on this. :)

But again, we can surely build on top of the support we have for int() now. Let's work out a plan for that.

Shaikh-Ubaid commented 6 months ago

int() will mean an arbitrary precision integer in future for LPython.

I think the type int would mean an arbitrary precision integer. For example, x: int. I think for now it is safe to use int() for string to int conversion.

Considering the case of a hexadecimal or an octal number, the result of something like i32(int("0x101100", 16))" will cause an error as the value is too large to fit in an i32.

Let's just focus on normal/decimal (base 10) integers for now. Once that is robustly supported, we can try supporting integers of other bases.

Shaikh-Ubaid commented 6 months ago

But again, we can surely build on top of the support we have for int() now. Let's work out a plan for that.

Sure, go ahead and make a plan. Figure out what is left to be supported for int() or what fails and you can work on fixing it.