package norm import "fmt" type op string type Expr struct { kind ExprKind ident *IdentExpr binary *BinExpr value *ValueExpr } func (e Expr) String() string { switch e.kind { case exprKindIdent: return e.ident.String() case exprKindBinary: return e.binary.String() case exprKindValue: return e.value.String() } // unreachable return "" } func (e Expr) Binds() []any { switch e.kind { case exprKindIdent: return e.ident.Binds() case exprKindBinary: return e.binary.Binds() case exprKindValue: return e.value.Binds() } // unreachable return nil } type ExprKind int const ( exprKindIdent ExprKind = iota exprKindBinary exprKindValue ) type IdentExpr string func (i IdentExpr) String() string { return string(i) } func (i IdentExpr) Binds() []any { return nil } func (i IdentExpr) AsExpr() Expr { return Expr{ kind: exprKindIdent, ident: &i, } } type ValueExpr struct { inner any } func (v ValueExpr) String() string { return "?" } func (v ValueExpr) Binds() []any { return []any{v.inner} } func (v ValueExpr) AsExpr() Expr { return Expr{ kind: exprKindValue, value: &v, } } type BinExpr struct { left Expr op op right Expr } func (b BinExpr) String() string { return fmt.Sprintf("(%s) %s (%s)", b.left.String(), b.op, b.right.String()) } func (b BinExpr) Binds() []any { binds := b.left.Binds() binds = append(binds, b.right.Binds()...) return binds } func (b BinExpr) AsExpr() Expr { return Expr{ kind: exprKindBinary, binary: &b, } } func buildBinExpr(left IdentExpr, op op, right any) Expr { return BinExpr{left.AsExpr(), op, ValueExpr{right}.AsExpr()}.AsExpr() } func Eq(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "=", right) } func Neq(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "<>", right) } func Gt(left string, right any) Expr { return buildBinExpr(IdentExpr(left), ">", right) } func Gte(left string, right any) Expr { return buildBinExpr(IdentExpr(left), ">=", right) } func Lt(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "<", right) } func Lte(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "<=", right) } func (l Expr) And(r Expr) Expr { return BinExpr{l, "AND", r}.AsExpr() } func (l Expr) Or(r Expr) Expr { return BinExpr{l, "OR", r}.AsExpr() }