Background
Combinatory logic is a system where a term is written using a finite set of combinators and function application between terms, and reduction rules are defined for each combinator. The well-known S and K combinators have the following reduction rules:
$$ \begin{aligned} S\;x\;y\;z & \overset{S}{\implies} x\;z\;(y\;z) \\ K\;x\;y & \overset{K}{\implies} x \end{aligned} $$
A term is in normal form when no reduction is possible. A term has a normal form if a series of reductions applied to it gives a normal form. The halting problem in combinatory logic is essentially about determining whether a term has a normal form.
It is known that any lambda calculus expression can be converted to an expression in SK combinatory logic, and therefore the halting problem for SK combinatory logic is undecidable. However, neither K nor S alone has such property, and it turns out that the halting problem for each of them is decidable. But K is too trivial (it always terminates), so we turn to S.
A paper titled The Combinator S, by Johannes Waldmann, describes how to decide if a term in S combinatory logic has a normal form. Theorem 55 is the main result, and the regular tree grammar is also presented in Appendix 2.
Examples
The expression \$S(SS)(SS)S\$ halts in two steps:
$$ \begin{aligned} S(SS)(SS)S & \overset{S}{\implies} SSS(SSS) \\ & \overset{S}{\implies} S(SSS)(S(SSS)) \\ \end{aligned} $$
But the two expressions \$S(SS)(SS)(S(SS)(SS))\$ and \$SSS(SSS)(SSS)\$ can be proven to be non-halting, as shown in the paper linked above.
Challenge
Solve the halting problem for S combinatory logic.
In this system, a term is simply a binary tree where each leaf represents S and each internal node represents application. You may take the input using any suitable structure that can directly represent the term, such as
- a binary tree data type,
- a nested array,
- or a string representation using parentheses (e.g.
S(SS)(SS)(SS)
or(((S(SS))(SS))(SS))
) or in prefix notation (e.g.@@@S@SS@SS@SS
).
For output, you can choose to
- output truthy/falsy using your language's convention (swapping is allowed), or
- use two distinct, fixed values to represent true (affirmative) or false (negative) respectively.
Standard code-golf rules apply. Shortest code in bytes wins.