C (gcc) (no builtins), 147147 143 bytes
4 bytes saved thanks to ceilingcat!
i,m,c,q,w;f(char*s){for(i=m=w=0;c=s[i]%9;i++)if(c-1?c-4?w=w*2+w+=w-~c%3,++m==4:(q=i,m=w=0):0)for(s[q]=w-2&&w<9?'>'62:'<';q++<i;60;q++<i;)s[q]=1;s=*s%3-1?*s:f(s);}
Function that takes a null-terminated char
array as input, and returns a char
(int
).
Here's a visualization of how it works:
(((>>>)<<)(<(<><)<)((<<(<<<))>(>(><>)<)))
((>....<<)(<<....<)((<<<....)>(>>....<)))
(>........<........(<........><........))
(>........<........<....................)
>........................................
(In the code, it uses 0x01 instead of .
, but due to the modulus, they're equivalent even in data representation.)
Essentially it scans the code looking for instances of (data)
, where data
consists of substring of 3 characters, except '.'
and '('
. (4, in the code, since it tracks the )
as the 4th character.) The program keeps track of the start of this string (q
) and a binary representation of the visited characters (w
). If we consider '>'
to be equivalent to the binary 1
, and '<'
the binary 0
, we simply look at a table of what the results of the binary strings should be. As it turns out, for q=1,4,5,6
, the answer is '<'
, and '>'
otherwise. Thus, w-1&&w<5
is a sufficient determiner. (In fact, since we keep track of )
as part of this binary string, we have to consider that everything is doubled, and for no byte cost, we can modify this formula to obtain the w-2&&w<9
.)
After we determine what the result should be, we replace the initial (
with the result, and all subsequent characters with .
. Then, so long as the initial character of the string is (
, we repeat our function. This allows us to skip moving the string around, which, in my head, should save bytes, but I haven't tested the alternative.
Started working on this before I saw the existing (much shorter) C answer. But I enjoyed this nonetheless.