func CompileFilter(filter string) (*Expression, error) {
	compiler := &filterCompiler{
		scan:    &filterScanner{},
		data:    append(copyOf(filter), 0, 0),
		off:     0,
		op:      scanFilterSkipSpace,
		opStack: make([]*Expression, 0),
		rsStack: make([]*Expression, 0),
	}
	compiler.scan.init()
	for compiler.hasMore() {
		step, err := compiler.next()
		if err != nil {
			return nil, err
		}
		if step == nil {
			break
		}
		if step.IsLiteral() || step.IsPath() {
			if err := compiler.pushBuildResult(step); err != nil {
				return nil, err
			}
			continue
		}
		switch compiler.pushOperator(step) {
		case pushOpOk:
			break
		case pushOpRightParenthesis:
			for {
				popped := compiler.popOperatorIf(func(top *Expression) bool {
					return !top.IsLeftParenthesis()
				})
				if popped != nil {
					_ = compiler.pushBuildResult(popped)
				} else {
					break
				}
			}
			if len(compiler.opStack) == 0 {
				return nil, fmt.Errorf("%w: mismatched parenthesis", spec.ErrInvalidFilter)
			} else {
				compiler.opStack = compiler.opStack[:len(compiler.opStack)-1]
			}
			break
		case pushOpInsufficientPriority:
			minPriority := opPriority(step.token)
			for {
				popped := compiler.popOperatorIf(func(top *Expression) bool {
					return opPriority(top.token) >= minPriority
				})
				if popped != nil {
					_ = compiler.pushBuildResult(popped)
				} else {
					break
				}
			}
			if compiler.pushOperator(step) != pushOpOk {
				panic("flaw in algorithm")
			}
			break
		}
	}
	for len(compiler.opStack) > 0 {
		_ = compiler.pushBuildResult(compiler.popOperatorIf(func(top *Expression) bool {
			return true
		}))
	}
	if len(compiler.rsStack) != 1 || !compiler.rsStack[0].IsOperator() {
		panic("flaw in algorithm")
	}
	root := compiler.rsStack[0]
	compiler.rsStack = nil
	return root, nil
}
CompileFilter compiles the given SCIM filter and return the root of the abstract syntax tree, or any error.

For example, for a filter such as:
(value eq "foo") and (primary ne true)
CompileFilter will return an abstract syntax tree in the structure of:
and
/    \
eq        \
/  \         \
value  "foo"     ne
/  \
primary true
package expr
import (
	"fmt"
	"github.com/imulab/go-scim/pkg/v2/spec"
	"strings"
)