The web version only has simple instructions since chapter 04, while the full book has detailed explanations and background info.

0503: Operator Priority

Multiple Priority Levels

This step adds * and /, which have higher precedence than + and -. Parentheses are ignored for now. In an expression that includes + - * /, addition and subtraction must be at the upper level (the root) of the tree. That is, subtrees of + and - can contain * and /. But without parentheses, subtrees of * and / cannot contain + or -. They can only be a value or column name, which is what parseAtom() handled before. So we need 2 changes:

  1. Copy parseAdd() and modify it slightly to create parseMul(), which parses expressions with only * and /.
  2. In parseAdd(), replace parseAtom() with parseMul(), so * and / become subtrees of + and -.

To parse an infix expression with multiple priority levels, start from the lowest priority. The highest priority is at the deepest level of the call chain. Now add parseExpr() as the entry point for expression parsing:

func (p *Parser) parseExpr() (interface{}, error) {
    return p.parseAdd()
}
func (p *Parser) parseAdd() (interface{}, error) {
    // call parseMul()
}
func (p *Parser) parseMul() (interface{}, error) {
    // call parseAtom()
}

There are only 2 priority levels here. To add more, repeat the same pattern.

Parentheses

Now add parentheses to change the order of operations. Parentheses can be seen as the highest priority operation, so they should be handled in the deepest call, parseAtom(). When a parenthesis is found, call the top-level parseExpr().

func (p *Parser) parseAtom() (expr interface{}, err error)

CodeCrafters.io has similar courses in many programming languages, including build your own Redis, SQLite, Docker, etc. It’s worth checking out.