⚠
The web version only has simple instructions since chapter 04, while the full book has detailed explanations and background info.
0501: Infix Expressions
Represent Expressions as Trees
To add range query to SQL, we need to parse more complex syntax. Earlier, we could only parse this fixed form of WHERE:
select a, b from t where a = 123 and b = 456;Now we need to support these forms:
select a, b from t where a > 123;
select a, b from t where (a, b) > (123, 0);We need a data structure to represent the expressions in WHERE:
type ExprBinOp struct {
op ExprOp
left interface{}
right interface{}
}For example:
// a = 123 and b = 456
ExprBinOp{op: OP_AND,
left: &ExprBinOp{op: OP_EQ,
left: "a",
right: &Cell{Type: TypeI64, I64: 123}},
right: &ExprBinOp{op: OP_EQ,
left: "b",
right: &Cell{Type: TypeI64, I64: 456}}}
// a > 123
ExprBinOp{op: OP_GT,
left: "a",
right: &Cell{Type: TypeI64, I64: 123}}Using interface{} can refer to any data type. It is similar to void * in C/C++, but it keeps type info, so it can be checked and cast back. Here we use 3 kinds of types:
- A string represents a column name.
&Cell{}represents a constant.&ExprBinOp{}represents a nested expression.
Parse Infix Expressions
In this step, we ignore operator priority and only parse expressions with +-:
// a
"a"
// a + b
&ExprBinOp{op: OP_ADD, left: "a", right: "b"}
// a + b - 3
&ExprBinOp{op: OP_SUB,
left: &ExprBinOp{op: OP_ADD, left: "a", right: "b"},
right: &Cell{Type: TypeI64, I64: 123}}The operands include column names and constants, and return interface{}:
func (p *Parser) parseAtom() (interface{}, error) {
if name, ok := p.tryName(); ok {
return name, nil
}
cell := &Cell{}
if err := p.parseValue(cell); err != nil {
return nil, err
}
return cell, nil
}Implement the parseAdd() function. It loops and calls parseAtom() and tryPunctuation():
func (p *Parser) parseAdd() (interface{}, error)ⓘ
CodeCrafters.io has similar courses in many programming languages, including build your own Redis, SQLite, Docker, etc. It’s worth checking out.