Parse a Scala expression Scala isn't a very commonly used language around here. Most of those who know it like it[citation needed], but some go :\ when they encounter its user-defined operators, saying they're too complicated.
However, they're governed by a very simple set of rules, outlined here. Their precedence depends on the first character. Here's the list for that (highest to lowest precedence):
* / %
+ -
:
= !
&lt; &gt;
&amp;
^
|
(all letters)

So this
a + b ^? c less a ==&gt; b | c

would be the same as this
((a + b) ^? c) less ((a ==&gt; b) | c)

Your task is to turn such an expression (only infix applications) into a tree-like structure or a string with all the sub-expressions in parentheses.
Input
A string or multiple characters given as an argument to a function, read from STDIN, given as command-line arguments, or using one of the other default input methods. This string is the expression to be parsed.
Output
You could do one of the following, printed to STDOUT, returned from a function, or one of the other default output methods:

The same string but with parentheses outside each sub-expression (the outermost expression may or may not be parenthesized). E.g., expr op expr2 op2 expr3 -&gt; (expr op expr2) op2 expr3. If you wish, you may also parenthesize the atoms ((((expr) op (expr2)) op2 (expr3)))
A multidimensional list, where each expression would broken up into the left argument, the operator/method, and the right argument. E.g., expr op expr2 op2 expr3 -&gt; [['expr','op','expr2'],'op2','expr3']
Some tree-like structure equivalent to the above 2 representations. You get the idea.

Rules

All operators used are binary, infix, and left-associative.
Parsing goes from left to right.
There will always be one or more spaces between arguments and operators.
Operators may consist of any of the symbols mentioned above (*/%+-:=!&lt;&gt;&amp;^|) and uppercase or lowercase letters([A-Za-z]). They will be one or more characters.
Arguments to methods may be other expressions or alphabetical identifiers ([A-Za-z]).
This is code-golf, so shortest code wins!

Test cases
More coming soon
Input                             -&gt; Output
a -- blah /\ foo                  -&gt; a -- (blah /\ foo)
same ** fst *^ chr *&amp; operators   -&gt; ((same ** fst) *^ chr) *&amp; operators
Lots   Of     SpAceS // here      -&gt; Lots Of (SpAceS // here)
Not : confusing * At / ALL iS it  -&gt; (Not : ((confusing * At) / ALL)) iS it
This *isnot* valid ** Scala       -&gt; (This *isnot* valid) ** Scala
JavaScript (ES6),  180 ... 155  152 bytes
Returns a multidimensional list. The outermost expression is parenthesized, and so are the atoms.

f=(i,a=i.split(/ +/))=&gt;&quot;w | ^ &amp; &lt;&gt; =! : +- */%&quot;.split` `.some(p=&gt;a.map((s,j)=&gt;i=!!s.match(`^[\\${p}]`)&amp;j?j:i)|i)?[f(i=a.splice(i),a),i.shift(),f(a,i)]:a

Try it online!
How?
This is a recursive algorithm. At each iteration, we look for the last operator with the lowest precedence, split the expression at this position and process recursive calls on both resulting parts. We stop the recursion when we reach an atom.
In order to split the expression and isolate the operator, we use a combination of splice() and shift() as shown in the following example, where integers are used instead of operators and operands.
a = [ 0, 1, 2, 3, 4, 5, 6 ];
i = 3;
i = a.splice(i); // --&gt; a[] = [ 0, 1, 2 ] (left expression)
                 //     i[] = [ 3, 4, 5, 6 ] (operator + right expression)
i.shift();       // --&gt; operator = 3
                 //     i[] = [ 4, 5, 6 ] (right expression)

Commented
f = (                      // f is a recursive function taking:
  i,                       //   i   = input string on the 1st iteration,
                           //         and then some non-empty array
  a = i.split(/ +/)        //   a[] = input string split on spaces
) =&gt;                       //         NB: operators are expected at odd positions
  &quot;w | ^ &amp; &lt;&gt; =! : +- */%&quot; // this string describes the groups of operators,
                           // from lowest to highest precedence
  .split` `                // split it
  .some(p =&gt;               // for each pattern p:
    a.map((s, j) =&gt;        //   for each string s at position j in a[]:
      i =                  //     update i:
        !!s.match(         //       see if s matches p; the '\' is required for
          `^[\\${p}]`      //       'w' and '^', and harmless for the other ones
        ) &amp; j ?            //       if there's a match and j is odd:
          j                //         update i to j
        :                  //       else:
          i                //         leave i unchanged
    )                      //   end of map()
    | i                    //   make some() succeed if i is a number
  ) ?                      // end of some(); if successful:
    [                      //   build a new array consisting of:
      f(                   //     the result of a recursive call ...
        i = a.splice(i), a //     ... with the left expression
      ),                   //
      i.shift(),           //     followed by the operator
      f(                   //     followed by the result of a recursive call ...
        a, i               //     ... with the right expression
      )                    //
    ]                      //   end of new array
  :                        // else:
    a                      //   just return a[]