AWK, 145 bytes
x=split($0,a,b){for(;""!=e=a[++b];d+=(e=="(")-(e==")"))for(c=0;c++<x;f=f<d?d:f)z[c]=z[c](c==d?e=="("||e==")"?" ":e:" ")}END{for(;f;)print z[f--]}
Try it online!
This code takes advantage of the spacing rules of the contest, which makes it possible to process single characters and build up a set of output lines one character at time.
For each character in the input string, one character is appended to all the output lines. All but one line get
(space) characters. Only the line associated with the current topological height get a non-space character.
Here's a breakdown...
x=split($0,a,b){ ... }
The input line is split into a character array stored in a
with the number of characters found saved in x
.
for(;""!=e=a[++b];d+=(e=="(")-(e==")"))
The code block is one statement, a for
loop with one statement, which is also a for
loop. The outer loop just iterates over all the characters. The top of loop test ""!=e=a[++b]
stores the current character in e
to save some characters later.
The annoying ""!=
check is needed to keep AWK from bailing if a 0
is included in the input...
The "end of loop" statement d+=(e=="(")-(e==")"
increments and decrements the d
topological depth indicator.
for(c=0;c++<x;f=f<d?d:f)
The inner for
loop appends a character to all the output strings the code is assembling. It uses x
(the number of characters in the input string) as the upper bound for the number of strings to construct since we don't know now many parenthesis are in the input. That's at least 2x too large, but the code is shorter if we just assemble lines that are never used.
The "end of loop" statement f=f<d?d:f
just sets f
to the maximum topological depth the code sees, which is used to figure out how many lines to print out later.
z[c]=z[c](c==d?e=="("||e==")"?" ":e:" ")
The body of the inner loop just appends a character to the current output string z[c]
. The input character is appended only if the current line matches the topological depth, and the character isn't a paren.
END{for(;f;)print z[f--]}
One all the characters have been processed, the code just prints all the assembled lines up and including the largest topological depth reached while parsing the input.
((1 2))))))))))3
should be invalid if negative heights are forbidden. \$\endgroup\$