# Parse a C++14 integer literal

According to http://en.cppreference.com/w/cpp/language/integer_literal, integer literals consist of a decimal/hex/octal/binary literal and a optional integer suffix, that is obviously completely unnecessary, wastes precious bytes and is not used in this challenge.

A decimal literal is a non-zero decimal digit (1, 2, 3, 4, 5, 6, 7, 8, 9), followed by zero or more decimal digits (0, 1, 2, 3, 4, 5, 6, 7, 8, 9).

A octal literal is the digit zero (0) followed by zero or more octal digits (0, 1, 2, 3, 4, 5, 6, 7).

A hexadecimal literal is the character sequence 0x or the character sequence 0X followed by one or more hexadecimal digits (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, A, b, B, c, C, d, D, e, E, f, F) (note the case-insensitivity of abcdefx).

A binary literal is the character sequence 0b or the character sequence 0B followed by one or more binary digits (0, 1).

Additionally, there may optionally be some 's as a digit separator. They have no meaning and can be ignored.

## Input

A string that represents a C++14 integer literal or an array of its charcodes.

## Output

The number represented by the input string in base 10, with an optional trailing newline. The correct output never will exceed 2*10^9

## Winning criteria

The GCC contributors need over 500 lines of code to do this, therefore our code must be as short as possible!

## Test cases:

0                       ->    0
1                       ->    1
12345                   ->    12345
12345'67890             ->    1234567890
0xFF                    ->    255
0XfF                    ->    255
0xAbCdEf                ->    11259375
0xa'bCd'eF              ->    11259375
0b1111'0000             ->    240
0b0                     ->    0
0B1'0                   ->    2
0b1                     ->    1
00                      ->    0
01                      ->    1
012345                  ->    5349
0'123'4'5               ->    5349

• Sandbox link – someone May 16 at 13:11
• @LuisfelipeDejesusMunoz No; how did you expect that to be parsed? – someone May 16 at 13:19
• I assume simply writing a function in C++14 would be cheating, right? Since the compiler already does it automatically (even if it is internally 500+ lines of code...) – Darrel Hoffman May 16 at 20:22
• @DarrelHoffman You couldn't just do it with "a function in C++14" though, since that wouldn't take a string input. Maybe with some script that invokes a C++ compiler. – aschepler May 16 at 21:05
• The string 0 might be a good test case to add (it revealed a bug in one of my recent revisions). – Daniel Schepler May 17 at 1:25

# x86 (32-bit) machine code, 59 57 bytes

This function takes esi as a pointer to a null-terminated string and returns the value in edx. (Listing below is GAS input in AT&T syntax.)

        .globl parse_cxx14_int
.text
parse_cxx14_int:
push $10 pop %ecx # store 10 as base xor %eax,%eax # initialize high bits of digit reader cdq # also initialize result accumulator edx to 0 lodsb # fetch first character cmp$'0', %al
jne .Lparseloop2
lodsb
and $~32, %al # uppercase letters (and as side effect, # digits are translated to N+16) jz .Lend # "0" string cmp$'B', %al           # after '0' have either digit, apostrophe,
# 'b'/'B' or 'x'/'X'
je .Lbin
jg .Lhex
dec %ecx
dec %ecx                # update base to 8
jmp .Lprocessdigit      # process octal digit that we just read (or
# skip ' if that is what we just read)
.Lbin:
sub $14, %ecx # with below will update base to 2 .Lhex: add$6, %ecx            # update base to 16
.Lparseloop:
lodsb                   # fetch next character
.Lparseloop2:
and $~32, %al # uppercase letters (and as side effect, # digits are translated to N+16) jz .Lend .Lprocessdigit: cmp$7, %al             # skip ' (ASCII 39 which would have been
# translated to 7 above)
je .Lparseloop
test $64, %al # distinguish letters and numbers jz .Lnum sub$39, %al            # with below will subtract 55 so e.g. 'A'==65
# will become 10
.Lnum:
sub $16, %al # translate digits to numerical value imul %ecx, %edx # movzbl %al, %eax add %eax, %edx # accum = accum * base + newdigit jmp .Lparseloop .Lend: ret  And a disassembly with byte counts - in Intel format this time, in case you prefer that one. Disassembly of section .text: 00000000 <parse_cxx14_int>: 0: 6a 0a push 0xa 2: 59 pop ecx 3: 31 c0 xor eax,eax 5: 99 cdq 6: ac lods al,BYTE PTR ds:[esi] 7: 3c 30 cmp al,0x30 9: 75 16 jne 21 <parse_cxx14_int+0x21> b: ac lods al,BYTE PTR ds:[esi] c: 24 df and al,0xdf e: 74 28 je 38 <parse_cxx14_int+0x38> 10: 3c 42 cmp al,0x42 12: 74 06 je 1a <parse_cxx14_int+0x1a> 14: 7f 07 jg 1d <parse_cxx14_int+0x1d> 16: 49 dec ecx 17: 49 dec ecx 18: eb 0b jmp 25 <parse_cxx14_int+0x25> 1a: 83 e9 0e sub ecx,0xe 1d: 83 c1 06 add ecx,0x6 20: ac lods al,BYTE PTR ds:[esi] 21: 24 df and al,0xdf 23: 74 13 je 38 <parse_cxx14_int+0x38> 25: 3c 07 cmp al,0x7 27: 74 f7 je 20 <parse_cxx14_int+0x20> 29: a8 40 test al,0x40 2b: 74 02 je 2f <parse_cxx14_int+0x2f> 2d: 2c 27 sub al,0x27 2f: 2c 10 sub al,0x10 31: 0f af d1 imul edx,ecx 34: 01 c2 add edx,eax 36: eb e8 jmp 20 <parse_cxx14_int+0x20> 38: c3 ret  And in case you want to try it, here is the C++ test driver code that I linked with it (including the calling convention specification in GCC asm syntax): #include <cstdio> #include <string> #include <iostream> inline int parse_cxx14_int_wrap(const char *s) { int result; const char* end; __asm__("call parse_cxx14_int" : "=d"(result), "=S"(end) : "1"(s) : "eax", "ecx", "cc"); return result; } int main(int argc, char* argv[]) { std::string s; while (std::getline(std::cin, s)) std::printf("%-16s -> %d\n", s.c_str(), parse_cxx14_int_wrap(s.c_str())); return 0; }  -1 byte due to comment by Peter Cordes -1 byte from updating to use two decrements to change 10 to 8 • Only you're missing tests for overflows... Too large a number gets reported by compilers. – Alexis Wilke May 17 at 4:38 • Can you swap your register usage for rdx and rbx? Then you can use 1-byte cdq to zero rdx from eax. – Peter Cordes 2 days ago • This should be either list the byte count of your assembly, or be labelled as 59 bytes of x86 machine code. – Potato44 2 days ago • @PeterCordes Thanks, didn't know about that one. (Also, on looking at it again, I noticed that changing the base from 10 to 8 could be 2 bytes - from two decrement instructions - instead of 3 bytes.) – Daniel Schepler 2 days ago • @AlexisWilke It also doesn't test for invalid format (e.g. digits out of range of the given base) which compilers would also do. But according to the problem statement, the input is guaranteed to be valid and not to overflow a 32-bit signed integer. – Daniel Schepler 2 days ago # JavaScript (Babel Node), 26 bytes lol x2 _=>eval(_.split'.join)  Try it online! • This isn't BabelJS exclusive, it works from ES6 onwards – Bassdrop Cumberwubwubwub May 16 at 13:29 • @BassdropCumberwubwubwub, the header was probably copied from TIO. – Shaggy May 16 at 17:41 • Nice, I first tried to use Number because it handles binary and hex, but apparently not octal Number("010") === 10 – Carl Walsh May 16 at 21:48 # C++ (gcc), 141138134 120 bytes This is a function that takes an array of characters (specified as a pair of pointers to the start and end - using the pair of iterators idiom) and returns the number. Note that the function mutates the input array. (This does rely on the behavior of gcc/libstdc++ that #include<cstdlib> also places the functions in global scope. For strictly standard compliant code, replace with #include<stdlib.h> for a cost of one more character.) Brief description: The code first uses std::remove to filter out ' characters (ASCII 39). Then, strtol with a base of 0 will already handle the decimal, octal, and hexadecimal cases, so the only other case to check for is a leading 0b or 0B and if so, set the base for strtol to 2 and start parsing after the leading 2 characters. #import<algorithm> #import<cstdlib> int f(char*s,char*e){e=s[*std::remove(s,e,39)=1]&31^2?s:s+2;return strtol(e,0,e-s);}  Try it online. Saved 3 bytes due to suggestion by ceilingcat and some more golfing that followed. Saved 4 bytes due to suggestions by grastropner. -2 bytes by Lucas -12 bytes by l4m2 • 134 bytes – gastropner 2 days ago • Incorporated, thanks. – Daniel Schepler 2 days ago • – Lucas 2 days ago • If invalid input is undefined behavior, no need to check if 1st char is 0 for base 2 – l4m2 yesterday • so 124 – l4m2 yesterday # Python 2, 32 bytes lambda a:eval(a.replace("'",""))  Try it online! lol (needs Python 2 because Python 3 changed octal literals to 0o(...)). • we've truly gone full circle at this point – osuka_ 2 days ago # Japt, 6 bytes OxUr"'  Try it online! # Perl 5 (-p), 14 bytes y/'/_/;$_=eval


TIO

# R, 7971 69 bytes

+=strtoi;s=gsub("'","",scan(,""));na.omit(c(+s,sub("..",0,s)+2))[1]


Try it online!

strtoi does everything except for the base 2 conversions and ignoring the ', so there's quite a lot of bytes just to fix those things.

Thanks to Aaron Hayman for -6 bytes, and inspiring -4 more bytes (and counting!)

Verify all test cases (old version)

• can save a byte replacing sub("0b|B" with sub("b|B", since the leading "0" will not affect the value. Can get another by renaming strtoi – Aaron Hayman May 16 at 14:29
• 74 bytes: Try it online! – Aaron Hayman May 16 at 14:52
• @AaronHayman wow, I've never seen na.omit before. Super handy here, and I golfed a bit more off :-) – Giuseppe May 16 at 15:00
• If we assume every fail of the first strtoi is a binary, you can use substring instead of sub to save another byte: Try it online! – Aaron Hayman May 16 at 15:18
• @AaronHayman we can strip off the first 2 characters of s using sub instead with sub('..','',s) which is another byte shorter! – Giuseppe May 16 at 15:40

# 05AB1E, 16 14 bytes

Saved 2 bytes thanks to Grimy

''KlÐïK>i8ö}.E


Try it online! or as a Test Suite

Explanation

''K                # remove "'" from input
l               # and convert to lower-case
Ð              # triplicate
ï             # convert one copy to integer
K            # and remove it from the second copy
>i  }       # if the result is 0
8ö        # convert from base-8 to base-10
.E     # eval

• -2 bytes – Grimy 2 days ago
• And here's a fake 13 (passes all the test cases, but fails on e.g. 0010). – Grimy 2 days ago
• @Grimy: Thanks! Cool use of ï! – Emigna 2 days ago

# Excel, 115 bytes

=DECIMAL(SUBSTITUTE(REPLACE(A1,2,1,IFERROR(VALUE(MID(A1,2,1)),)),"'",),VLOOKUP(A1,{"0",8;"0B",2;"0X",16;"1",10},2))


Input from A1, output to wherever you put this formula. Array formula, so use Ctrl+Shift+Enter to enter it.

I added a couple test cases you can see in the image - some early attempts handled all given test cases correctly but got rows 16 and/or 17 wrong.

# x86-64 machine code, 44 bytes

(The same machine code works in 32-bit mode as well.)

@Daniel Schepler's answer was a starting point for this, but this has at least one new algorithmic idea (not just better golfing of the same idea): The ASCII codes for 'B' (1000010) and 'X' (1011000) give 16 and 2 after masking with 0b0010010.

So after excluding decimal (non-zero leading digit) and octal (char after '0' is less than 'B'), we can just set base = c & 0b0010010 and jump into the digit loop.

Callable with x86-64 System V as unsigned __int128 parse_cxx14_int(int dummy, const char*rsi); Extract the EDX return value from the high half of the unsigned __int128 result with tmp>>64.

        .globl parse_cxx14_int
## Input: pointer to 0-terminated string in RSI
## output: integer in EDX
## clobbers: RAX, RCX (base), RSI (points to terminator on return)
parse_cxx14_int:
xor %eax,%eax           # initialize high bits of digit reader
cdq                     # also initialize result accumulator edx to 0
lea 10(%rax), %ecx      # base 10 default
lodsb                   # fetch first character
cmp $'0', %al jne .Lentry2 # leading zero. Legal 2nd characters are b/B (base 2), x/X (base 16) # Or NUL terminator = 0 in base 10 # or any digit or ' separator (octal). These have ASCII codes below the alphabetic ranges lodsb mov$8, %cl              # after '0' have either digit, apostrophe, or terminator,
cmp    $'B', %al # or 'b'/'B' or 'x'/'X' (set a new base) jb .Lentry2 # enter the parse loop with base=8 and an already-loaded character # else hex or binary. The bit patterns for those letters are very convenient and$0b0010010, %al      # b/B -> 2,   x/X -> 16
xchg   %eax, %ecx
jmp  .Lentry

.Lprocessdigit:
sub  $'0' & (~32), %al jb .Lentry # chars below '0' are treated as a separator, including ' cmp$10, %al
jb  .Lnum
add  $('0'&~32) - 'A' + 10, %al # digit value = c-'A' + 10. we have al = c - '0'&~32. # c = al + '0'&~32. val = m+'0'&~32 - 'A' + 10 .Lnum: imul %ecx, %edx add %eax, %edx # accum = accum * base + newdigit .Lentry: lodsb # fetch next character .Lentry2: and$~32, %al           # uppercase letters (and as side effect,
# digits are translated to N+16)
jnz .Lprocessdigit      # space also counts as a terminator
.Lend:
ret


The changed blocks vs. Daniel's version are (mostly) indented less than other instruction. Also the main loop has its conditional branch at the bottom. This turned out to be a neutral change because neither path could fall into the top of it, and the dec ecx / loop .Lentry idea for entering the loop turned out not to be a win after handling octal differently. But it has fewer instructions inside the loop with the loop in idiomatic form do{}while structure, so I kept it.

Daniel's C++ test harness works unchanged in 64-bit mode with this code, which uses the same calling convention as his 32-bit answer.

g++ -Og parse-cxx14.cpp parse-cxx14.s &&
./a.out < tests | diff -u -w - tests.good


Disassembly, including the machine code bytes that are the actual answer

0000000000000000 <parse_cxx14_int>:
0:   31 c0                   xor    %eax,%eax
2:   99                      cltd
3:   8d 48 0a                lea    0xa(%rax),%ecx
6:   ac                      lods   %ds:(%rsi),%al
7:   3c 30                   cmp    $0x30,%al 9: 75 1c jne 27 <parse_cxx14_int+0x27> b: ac lods %ds:(%rsi),%al c: b1 08 mov$0x8,%cl
e:   3c 42                   cmp    $0x42,%al 10: 72 15 jb 27 <parse_cxx14_int+0x27> 12: 24 12 and$0x12,%al
14:   91                      xchg   %eax,%ecx
15:   eb 0f                   jmp    26 <parse_cxx14_int+0x26>
17:   2c 10                   sub    $0x10,%al 19: 72 0b jb 26 <parse_cxx14_int+0x26> 1b: 3c 0a cmp$0xa,%al
1d:   72 02                   jb     21 <parse_cxx14_int+0x21>
1f:   04 d9                   add    $0xd9,%al 21: 0f af d1 imul %ecx,%edx 24: 01 c2 add %eax,%edx 26: ac lods %ds:(%rsi),%al 27: 24 df and$0xdf,%al
29:   75 ec                   jne    17 <parse_cxx14_int+0x17>
2b:   c3                      retq


Other changes from Daniel's version include saving the sub $16, %al from inside the digit-loop, by using more sub instead of test as part of detecting separators, and digits vs. alphabetic characters. Unlike Daniel's every character below '0' is treated as a separator, not just '\''. (Except ' ': and$~32, %al / jnz in both our loops treats space as a terminator, which is possibly convenient for testing with an integer at the start of a line.)

Every operation that modifies %al inside the loop has a branch consuming flags set by the result, and each branch goes (or falls through) to a different location.

x=${1//\'};echo$[${x/#0[Bb]/2#}]  TIO # Zsh, 29 bytes echo$[${${1//\'}/#0[Bb]/2#}]


TIO

# Retina, 96 bytes

T'L_l
\B
:
^
a;
a;0:x:
g;
a;0:b:
2;
a;0:
8;
[a-g]
1$& Tld +;(\d+):(\d+) ;$.($*$1*_$2* .+;  Try it online! Link includes test suite. Explanation: T'L_l  Delete 's and convert everything to lower case. \B :  Separate the digits, as any hex digits need to be converted into decimal. ^ a; a;0:x: g; a;0:b: 2; a;0: 8;  Identify the base of the number. [a-g] 1$&
Tld


Convert the characters a-g into numbers 10-16.

+;(\d+):(\d+)
;$.($*$1*_$2*


Perform base conversion on the list of digits. $.($*$1*_*$2* is short for $.($*$1*_*$2*_) which multiplies $ and $1 together and adds $2. ($ is the part of the string before the ; i.e. the base.)

.+;



Delete the base.

• I appreciate the literal programming approach you took to explain the code :-) – grooveplex 2 days ago

# J, 48 bytes

cut@'0x 16b +0b 2b +0 8b0 '''do@rplc~'+',tolower


Try it online!

Eval after string substitution.

0XfF -> +16bff -> 255
0xa'bCd'eF -> +16babcdef -> 11259375
0B1'0 -> +2b10 -> 2
0 -> 8b0 -> 0
01 -> 8b01 -> 1
0'123'4'5 -> 8b012345 -> 5349

• It doesn't seem to work correctly with hexadecimals containing 0b: tio.run/##FcwxCsIwFAbg/… – Galen Ivanov May 16 at 19:21
• @GalenIvanov nice find, fixed – FrownyFrog May 16 at 19:35

# Octave, 2921 20 bytes

@(x)str2num(x(x>39))


Try it online!

-8 bytes thanks to @TomCarpenter

• For 22 bytes: @(x)str2num(x(x~="'")) – Tom Carpenter 2 days ago
• Which becomes for 21 bytes: @(x)str2num(x(x~=39)) – Tom Carpenter 2 days ago

# Go, 75

import "strconv"
func(i string)int64{n,_:=strconv.ParseInt(i,0,0);return n}

• This doesn't appear to work for binary literals, nor for single-quote digit delimiters. – kundor 2 days ago

# JavaScript (ES6), 112 bytes

n=>+(n=n.toLowerCase().replace(/'/g,""))?n[1]=="b"?parseInt(n.substr(2),2):parseInt(n,+n[0]?10:n[1]=="x"?16:8):0


# Jelly, 27 bytes

ØDiⱮḢ=1aƲȦ
ṣ”';/Ḋ⁾0o;Ɗ¹Ç?ŒV


Try it online!

Almost all of this is handling octal. Feels like it could be better golfed.

# Ruby with -n, 17 bytes

Just jumping on the eval train, really.

p eval gsub(?'){}


Try it online!

# Java (JDK), 101 bytes

n->{n=n.replace("'","");return n.matches("0[bB].+")?Long.parseLong(n.substring(2),2):Long.decode(n);}


Try it online!

Long.decode deals with all kinds of literals except the binary ones.

• Nice. I need to look more at the functions primitive wrappers have – Benjamin Urquhart yesterday

# Perl 6, 29 bytes

{+lc S/^0)>\d/0o/}o{S:g/\'//}


Try it online!

Perl 6 requires an explicit 0o prefix for octal and doesn't support uppercase prefixes like 0X.

### Explanation

                   {S:g/\'//}  # remove apostrophes
{                }o  # combine with function
S/^0)>\d/0o/    # 0o prefix for octal
lc  # lowercase
+    # convert to number


# C (gcc), 120 118 bytes

-1 byte thanks to ceilingcat

f(char*s){int r=0,c=s[1]&31,b=10;for(s+=2*(*s<49&&(b=c^24?c^2?8:2:16)-8);c=*s++;)r=c^39?r*b+(c>57?c%32+9:c-48):r;c=r;}


Try it online!

# Java, 158 154 bytes

This just waiting to be outgolfed. Literally just trys regexes until something works and default to hex.
-4 bytes thanks to @ValueInk

n->{n=n.replace("'","");var s=n.split("[bBxX]");return Long.parseLong(s[s.length-1],n.matches("0[bB].+")?2:n.matches("0\\d+")?8:n.matches("\\d+")?10:16);}


# Using ScriptEngine, 92 87 bytes

Eval train coming through. Technically this is passing the torch to JS, so it's not my main submission.

n->new javax.script.ScriptEngineManager().getEngineByName("js").eval(n.replace("'",""))


TIO

• Use [bBxX] and 0[bB].+` for some quick regex optimizations. – Value Ink May 17 at 0:09
• @ValueInk thanks – Benjamin Urquhart May 17 at 0:15