x86-64 machine code,
34 33 bytes
B8 66 41 AD AC AC 24 07 9E 92 66 98 7A 0A 72 03 F6 E2 92 FF CF 7B EA B8 F6 F2 84 E4 74 F4 24 80 C3
Try it online!
Following the standard calling convention for Unix-like systems (from the System V AMD64 ABI), this takes the length of the string (9) in RDI and its address in RSI, and returns a value in AL, 128 if the expression is Python 2/3 safe and 0 if not.
f: .byte 0xB8
This will be explained later.
n: .byte 0x66, 0x41, 0xAD
When jumping to
n, the current number will be in DL.
This is a
lodsw instruction, encoded in a strange way for reasons to be explained later. This loads two bytes from the string into AX, advancing the pointer; in particular, the operation symbol goes into AH.
Load another byte (a space) from the string into AL.
Load another byte (a digit) from the string into AL.
and al, 7
Take the low three bits, obtaining the numerical value of the digit in AL.
Set certain flags based on certain bits of AH, which contains the operation symbol. In particular:
xchg edx, eax
Sign-extend AL (previous number); this makes AH 0.
Jump if PF=1, which is true only for
Jump if CF is 1, which is true for
+ but not for
*. An addition breaks a sequence of
/, making the previous number irrelevant; the current number is correctly in DL.
* is left here.
Multiply AL (previous number) by DL (new digit).
s: xchg edx, eax
Exchange registers. The product goes into DL.
t: dec edi
+ joins here.) Subtract 1 from EDI.
Jump if the sum of the low 8 bits is odd. This is true the first two times, with 8 (1000₂) and 7 (111₂), but not true the third time, with 6 (110₂).
If we get here, the expression is Python 2/3 safe.
This subsumes the next two instructions into
mov eax, 0xE484F2F6.
d: div dl
/ jumps here.)
Divide AX by DL, putting the quotient in AL and the remainder in AH.
test ah, ah
Set flags based on the value of AH.
If the remainder is 0, jump (and next, exchange the quotient into DL and proceed).
Also, the success case joins here. In that case, the zero flag was last set by
dec edi, which gave a nonzero result, so the jump is not taken.
and al, 0x80
Keep the high bit of AL.
In the success case, the high bit is 1 from the
In the failure case, AL is the quotient, which is at most 12 (from 25/2), so the high bit is 0.
Now, the start can be explained. The first byte 0xB8 subsumes the next four bytes into
mov eax, 0xACAD4166. Those bytes include a
lodsw instruction, which was encoded with a nonfunctional REX.B prefix (0x41) so that that value goes into AH. After that, the first digit is read by
lodsb. The 0x41 value, which is in the same place as an operation symbol would be in later iterations, has a 1 in bit 0 and a 0 in bit 2, so it gets treated in the same way as a
+, making the first digit alone become the current number.