Clearly parenthesize APL trains In APL, you can write tacit functions, called trains. How they work is irrelevant for this challenge. Here are the different ways they can be grouped, using ⍴ as the function:

⍴      -&gt; ⍴
⍴⍴     -&gt; ⍴⍴
⍴⍴⍴    -&gt; ⍴⍴⍴
⍴⍴⍴⍴   -&gt; ⍴(⍴⍴⍴)
⍴⍴⍴⍴⍴  -&gt; ⍴⍴(⍴⍴⍴)
⍴⍴⍴⍴⍴⍴ -&gt; ⍴(⍴⍴(⍴⍴⍴))
...


The order remains the same. The procedure is that as long as there are strictly more than 3 functions, the last 3 functions are grouped into one function. If we meet a nested train, we parenthesize that first, before continuing. Here is the procedure applied to ⍴⍴⍴⍴⍴⍴:

Step 0: ⍴⍴⍴⍴⍴⍴
There are strictly more than 3 functions, repeat.
Step 1: ⍴⍴⍴(⍴⍴⍴)
There are strictly more than 3 functions, repeat.
Step 2: ⍴(⍴⍴(⍴⍴⍴))
There are 3 or less functions, we're done.


Here is the same procedure applied to ⍴⍴⍴(⍴⍴)⍴(⍴⍴⍴⍴(⍴⍴⍴))⍴⍴:

Step 0: ⍴⍴⍴(⍴⍴)⍴(⍴⍴⍴⍴(⍴⍴⍴))⍴⍴
There are strictly more than 3 functions, repeat.
We have met a nested train, applying procedure to that first:
  Step 0: ⍴⍴⍴⍴(⍴⍴⍴)
  There are strictly more than 3 functions, repeat.
  We have met a nested train, applying procedure to that first:
    Step 0: ⍴⍴⍴
    There are 3 or less functions, we're done.
  Step 1: ⍴⍴(⍴⍴(⍴⍴⍴))
  There are 3 or less functions, we're done.
Step 1: ⍴⍴⍴(⍴⍴)⍴((⍴⍴(⍴⍴(⍴⍴⍴)))⍴⍴)
There are strictly more than 3 functions, repeat.
We have met a nested train, applying procedure to that first:
  Step 0: ⍴⍴
  There are 3 or less functions, we're done.
Step 2: ⍴⍴⍴((⍴⍴)⍴((⍴⍴(⍴⍴(⍴⍴⍴)))⍴⍴))
There are strictly more than 3 functions, repeat.
Step 3: ⍴(⍴⍴((⍴⍴)⍴((⍴⍴(⍴⍴(⍴⍴⍴)))⍴⍴)))
There are 3 functions or less, we're done.


Input

For this challenge, input will be simplified. This means that you can choose 2 different chars for opening and closing parentheses and 1 char for functions, different from the ones chosen for parentheses. The chars you choose must be consistent. The input will not be empty, and will not contain parentheses with no content (i.e. ()).

Output

Again, you may choose 3 different chars, 2 for parentheses and 1 for functions. Note that they need not be the same as the ones chosen for input, but they must be consistent.

Rules


If there are parentheses which only enclose one function within them in the input, you must remove them in the output. Your output may not contain unneeded parentheses (i.e. enclosing only one function, or enclosing the whole output).
You don't need to implement the algorithm used here, as long as your solution is valid for this challenge.
Input and output are strings in the format explained in the Input and Output sections. The input will have at least one character.
Using the standard loopholes is strictly forbidden.
This is code-golf, so shortest answer wins. However, there won't be an accepted answer, since this is a per-language competition, and to encourage answering in languages in which this task would result in longer code compared to code written in other languages.


Test cases

The chars used here are ()⍴, you should replace them with your chosen chars.

⍴                          -&gt; ⍴
⍴                          -&gt; ⍴
⍴⍴                         -&gt; ⍴⍴
⍴⍴⍴                        -&gt; ⍴⍴⍴
⍴⍴⍴⍴                       -&gt; ⍴(⍴⍴⍴)
⍴⍴⍴⍴⍴⍴⍴⍴⍴⍴⍴⍴⍴⍴⍴            -&gt; ⍴⍴(⍴⍴(⍴⍴(⍴⍴(⍴⍴(⍴⍴(⍴⍴⍴))))))
⍴⍴⍴⍴⍴(⍴⍴⍴)⍴⍴(⍴(⍴⍴⍴)⍴⍴⍴)⍴⍴⍴ -&gt; ⍴(⍴⍴(⍴⍴((⍴⍴⍴)⍴(⍴(⍴(⍴⍴⍴)(⍴⍴⍴))(⍴⍴⍴)))))
(⍴⍴⍴)(⍴⍴⍴)(⍴⍴⍴)            -&gt; (⍴⍴⍴)(⍴⍴⍴)(⍴⍴⍴)
(⍴⍴⍴)(⍴⍴⍴)⍴⍴⍴              -&gt; (⍴⍴⍴)(⍴⍴⍴)(⍴⍴⍴)
⍴⍴(⍴)⍴⍴                    -&gt; ⍴⍴(⍴⍴⍴)
((⍴⍴))                     -&gt; ⍴⍴
⍴⍴((⍴⍴))⍴⍴                 -&gt; ⍴⍴((⍴⍴)⍴⍴)


This challenge has been posted in the Sandbox. If you have the required privilege, you can view the sandbox post here.
APL (Dyalog Classic), 71 68 65 63 bytes



0{⍵≡⍕⍵:⍵⋄⍬≡⍵:'⍬'⋄1=≢⍵:⍺∇⊃⍵⋄3≥≢⍵:⍺⌽')(',⍣⍺∊1∇¨⍵⋄⍺∇¯3(↓,∘⊂1∇↑)⍵}⍎


Try it online!

The characters I chose for I/O are '(', ')', and '⍬'.

This solution is itself an APL train.

⍎ parses the input as if it's a nested array - a tree with empty numeric vectors (⍬) as leaves.

The dfn (i.e. lambda - { }) traverses the tree recursively and converts it to a properly parenthesised string. The left argument ⍺ controls whether parentheses should be added to the current level if necessary.

The dfn handles the following cases based on the right argument:


if it's already a string (⍵≡⍕⍵), return it
if it's ⍬, return the char '⍬'
if it's a singleton, just dig deeper (∇ is the symbol for a recursive call)
if its length is ≤3, recurse for each of the items and surround with () if necessary
otherwise, recurse for the 3-tail, prepend all but the 3-tail, and recurse again