-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
interpreter: Add hex/octal/binary support to str and int primitives #15250
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,12 +55,52 @@ def is_odd_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: | |
|
|
||
| @typed_kwargs( | ||
| 'to_string', | ||
| KwargInfo('fill', int, default=0, since='1.3.0') | ||
| KwargInfo('fill', int, default=0, since='1.3.0'), | ||
| KwargInfo( | ||
| "format", | ||
| str, | ||
| default="dec", | ||
| since="1.10.0", | ||
| validator=lambda x: ( | ||
| 'format must be "dec", "hex", "oct", or "bin"' | ||
| if x not in ("dec", "hex", "oct", "bin") | ||
| else None | ||
| ), | ||
| ), | ||
| ) | ||
| @noPosargs | ||
| @InterpreterObject.method('to_string') | ||
| def to_string_method(self, args: T.List[TYPE_var], kwargs: T.Dict[str, T.Any]) -> str: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While we're here, can you add a |
||
| return str(self.held_object).zfill(kwargs['fill']) | ||
| format_type = kwargs["format"] | ||
| fill = kwargs["fill"] | ||
|
|
||
| format_codes = {"hex": "#x", "oct": "#o", "bin": "#b", "dec": "d"} | ||
|
|
||
| if format_type == "dec": | ||
| result = str(self.held_object) | ||
| if fill > 0: | ||
| result = result.zfill(fill) | ||
| else: | ||
| result = format(self.held_object, format_codes[format_type]) | ||
|
|
||
| if fill > 0: | ||
| if result.startswith("-"): | ||
| sign = "-" | ||
| prefixed = result[1:] | ||
| else: | ||
| sign = "" | ||
| prefixed = result | ||
|
|
||
| prefix = prefixed[:2] | ||
| digits = prefixed[2:] | ||
|
|
||
| total_prefix_len = len(sign) + len(prefix) | ||
| if fill > total_prefix_len: | ||
| digits = digits.zfill(fill - total_prefix_len) | ||
|
|
||
| result = sign + prefix + digits | ||
|
Comment on lines
+79
to
+101
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't this all be implemented as: template = '0=#{}{}'.format(fill, format_codes[format_code))
return format(self.held_object, template)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even easier: return '{0:#0{fill}{format}}'.format(self.held_object, fill=fill, format=format_codes[format_code]) |
||
|
|
||
| return result | ||
|
|
||
| @typed_operator(MesonOperator.DIV, int) | ||
| @InterpreterObject.operator(MesonOperator.DIV) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| project('test number base conversions') | ||
|
|
||
| ### .to_int() | ||
|
|
||
| # hexadecimal strings | ||
| assert('0x10'.to_int() == 16, 'Hex string conversion failed') | ||
| assert('0X10'.to_int() == 16, 'Hex string conversion (uppercase X) failed') | ||
| assert('0xff'.to_int() == 255, 'Hex string conversion (lowercase) failed') | ||
| assert('0xFF'.to_int() == 255, 'Hex string conversion (uppercase) failed') | ||
| assert('0xDEADBEEF'.to_int() == 3735928559, 'Large hex conversion failed') | ||
| assert('0x1'.to_int() == 1, 'Small hex conversion failed') | ||
| assert('0x0'.to_int() == 0, 'Zero hex conversion failed') | ||
|
|
||
| # signed hexadecimal strings | ||
| assert('-0xf'.to_int() == -15, 'Negative hex string conversion failed') | ||
| assert('+0x10'.to_int() == 16, 'Positive hex string conversion failed') | ||
| assert('-0xFF'.to_int() == -255, 'Negative hex string (uppercase) conversion failed') | ||
|
|
||
| # octal strings | ||
| assert('0o10'.to_int() == 8, 'Octal string conversion failed') | ||
| assert('0O10'.to_int() == 8, 'Octal string conversion (uppercase O) failed') | ||
| assert('0o77'.to_int() == 63, 'Octal string conversion failed') | ||
| assert('0o755'.to_int() == 493, 'Octal permission-like conversion failed') | ||
| assert('0o0'.to_int() == 0, 'Zero octal conversion failed') | ||
|
|
||
| # signed octal strings | ||
| assert('-0o10'.to_int() == -8, 'Negative octal string conversion failed') | ||
| assert('+0o77'.to_int() == 63, 'Positive octal string conversion failed') | ||
|
|
||
| # binary strings | ||
| assert('0b10'.to_int() == 2, 'Binary string conversion failed') | ||
| assert('0B10'.to_int() == 2, 'Binary string conversion (uppercase B) failed') | ||
| assert('0b1111'.to_int() == 15, 'Binary string conversion failed') | ||
| assert('0b11111111'.to_int() == 255, 'Binary byte conversion failed') | ||
| assert('0b0'.to_int() == 0, 'Zero binary conversion failed') | ||
|
|
||
| # signed binary strings | ||
| assert('-0b101'.to_int() == -5, 'Negative binary string conversion failed') | ||
| assert('+0b1111'.to_int() == 15, 'Positive binary string conversion failed') | ||
|
|
||
| # decimal strings (backwards compat) | ||
| assert('10'.to_int() == 10, 'Decimal string conversion failed') | ||
| assert('255'.to_int() == 255, 'Decimal string conversion failed') | ||
| assert('0'.to_int() == 0, 'Zero decimal conversion failed') | ||
| assert('12345'.to_int() == 12345, 'Large decimal conversion failed') | ||
|
|
||
| # leading zeroes are decimal (backwards compat) | ||
| assert('010'.to_int() == 10, 'Decimal with leading zero broke backward compatibility') | ||
| assert('0123'.to_int() == 123, 'Decimal with leading zeros broke backward compatibility') | ||
| assert('007'.to_int() == 7, 'Decimal with leading zeros broke backward compatibility') | ||
|
|
||
| ### .to_string() | ||
|
|
||
| # hex format | ||
| assert(16.to_string(format: 'hex') == '0x10', 'Int to hex string failed') | ||
| assert(255.to_string(format: 'hex') == '0xff', 'Int to hex string failed') | ||
| assert(0.to_string(format: 'hex') == '0x0', 'Zero to hex string failed') | ||
| assert(1.to_string(format: 'hex') == '0x1', 'One to hex string failed') | ||
| assert(3735928559.to_string(format: 'hex') == '0xdeadbeef', 'Large hex string failed') | ||
|
|
||
| # octal format | ||
| assert(8.to_string(format: 'oct') == '0o10', 'Int to octal string failed') | ||
| assert(63.to_string(format: 'oct') == '0o77', 'Int to octal string failed') | ||
| assert(493.to_string(format: 'oct') == '0o755', 'Permission to octal string failed') | ||
| assert(0.to_string(format: 'oct') == '0o0', 'Zero to octal string failed') | ||
|
|
||
| # binary format | ||
| assert(2.to_string(format: 'bin') == '0b10', 'Int to binary string failed') | ||
| assert(15.to_string(format: 'bin') == '0b1111', 'Int to binary string failed') | ||
| assert(255.to_string(format: 'bin') == '0b11111111', 'Byte to binary string failed') | ||
| assert(0.to_string(format: 'bin') == '0b0', 'Zero to binary string failed') | ||
|
|
||
| # decimal format (explicit) | ||
| assert(10.to_string(format: 'dec') == '10', 'Int to decimal string failed') | ||
| assert(255.to_string(format: 'dec') == '255', 'Int to decimal string failed') | ||
|
|
||
| # default | ||
| assert(42.to_string() == '42', 'Default int to string failed') | ||
|
|
||
| # fill and hex format | ||
| assert(255.to_string(format: 'hex', fill: 8) == '0x0000ff', 'Hex with fill failed') | ||
| assert(1.to_string(format: 'hex', fill: 6) == '0x0001', 'Hex with fill failed') | ||
| assert(255.to_string(format: 'hex', fill: 4) == '0xff', 'Hex with fill (no padding needed) failed') | ||
|
|
||
| # fill and other formats | ||
| assert(8.to_string(format: 'oct', fill: 6) == '0o0010', 'Octal with fill failed') | ||
| assert(2.to_string(format: 'bin', fill: 10) == '0b00000010', 'Binary with fill failed') | ||
|
|
||
| # negative numbers | ||
| assert((-15).to_string(format: 'hex') == '-0xf', 'Negative hex conversion failed') | ||
| assert((-8).to_string(format: 'oct') == '-0o10', 'Negative octal conversion failed') | ||
| assert((-5).to_string(format: 'bin') == '-0b101', 'Negative binary conversion failed') | ||
|
|
||
| # negative numbers and fill | ||
| assert((-15).to_string(format: 'hex', fill: 6) == '-0x00f', 'Negative hex with fill failed') | ||
| assert((-8).to_string(format: 'oct', fill: 7) == '-0o0010', 'Negative octal with fill failed') | ||
| assert((-5).to_string(format: 'bin', fill: 8) == '-0b00101', 'Negative binary with fill failed') | ||
| assert((-4).to_string(fill: 3) == '-04', 'Negative decimal with fill failed') | ||
|
|
||
| # fill and decimal | ||
| assert(4.to_string(fill: 3) == '004', 'Decimal with fill failed') | ||
|
|
||
| ### Round trip conversions | ||
|
|
||
| # positive | ||
|
|
||
| hex_val = 0x200 | ||
| hex_str = hex_val.to_string(format: 'hex') | ||
| assert(hex_str.to_int() == hex_val, 'Hex round-trip failed') | ||
|
|
||
| oct_val = 0o755 | ||
| oct_str = oct_val.to_string(format: 'oct') | ||
| assert(oct_str.to_int() == oct_val, 'Octal round-trip failed') | ||
|
|
||
| bin_val = 0b11010 | ||
| bin_str = bin_val.to_string(format: 'bin') | ||
| assert(bin_str.to_int() == bin_val, 'Binary round-trip failed') | ||
|
|
||
| # negative | ||
|
|
||
| neg_hex = -255 | ||
| neg_hex_str = neg_hex.to_string(format: 'hex') | ||
| assert(neg_hex_str.to_int() == neg_hex, 'Negative hex round-trip failed') | ||
|
|
||
| neg_oct = -63 | ||
| neg_oct_str = neg_oct.to_string(format: 'oct') | ||
| assert(neg_oct_str.to_int() == neg_oct, 'Negative octal round-trip failed') | ||
|
|
||
| neg_bin = -15 | ||
| neg_bin_str = neg_bin.to_string(format: 'bin') | ||
| assert(neg_bin_str.to_int() == neg_bin, 'Negative binary round-trip failed') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use the
in_set_validator({...})for this.