If you want to handle both unary and binary operators as well as detecting all possible errors, the following is an overview of the Double-E approach to parsing infix expressions:
Create an empty Operand Stack — note that if parsing for AST then this is a stack of ASTs.
Create an empty Operator Stack — note that the operator enum represents true operators in the language being parsed, not tokens (so, operators here are not simply "+" but differentiated as unary + vs. binary +).
Start in Unary State.
- Unary State:
- if you find an operand, push that on the Operand Stack and switch to Binary State.
- if you find an operator, say +, then it is unary +, so push unary + onto the Operator Stack and stay in Unary State
- if you find an open paren (, then push grouping paren onto the Operator Stack, stay in Unary State
- if you find end-of-input, then error, incomplete parse
- Binary State
- if you find end of input, then Exit State
- if you find an operand, this is juxtaposition, which is an error in most languages, but you can do something useful if desired.
- if you find an operator, say +, then it is a binary +, so Reduce, then push binary + onto the Operator Stack and switch to Unary State.
- if you find a close paren ), then Reduce until matching ) (which could be a function call Operator or grouping paren Operator), stay in Binary State
- Exit State
- Reduce all operators on the Operator Stack by taking their operands off of the Operand Stack
- when finished, should have no operators on Operator Stack, and exactly one operand on the Operand Stack, and that one operand is the answer to the parse.
Differentiation between unary and binary state allows for the industrial strength of this approach, and without that the knowledge of which state, many things cannot be differentiated, and/or error conditions detected.
Reduction, when called for, and which happens for a number of different reasons, is a matter of assembling recognized piece parts into a larger whole. In essence, pulling an operator off of the Operator Stack, and as many operands as that operator takes off of the Operand Stack, assembling those into a recognized unit, such as with an AST fragment, and pushing that onto the Operand Stack. Reduction continues until some limit is reached such as empty Operand Stack (used in End State), or the precedence of the current operator is higher than that of the operator on the top of the Operator Stack.
A more formal writeup can be found here.
This approach can handle unary operators, binary operators, pre/post fix unary, grouping parenthesis, functions & function invocation, array references, casts, etc.. This approach is industrial strength (i.e. without the many short comings in Shunting Yard, for example).