diff --git a/internal/filter/parser.go b/internal/filter/parser.go index 1ce642bf..d4fd864d 100644 --- a/internal/filter/parser.go +++ b/internal/filter/parser.go @@ -1,357 +1,2401 @@ +// Code generated by pigeon; DO NOT EDIT. + package filter import ( + "bytes" + "errors" "fmt" + "io" + "io/ioutil" + "math" "net/url" + "os" + "sort" + "strconv" "strings" + "unicode" + "unicode/utf8" ) -type Parser struct { - tag string - pos, length, openParenthesis int +// ParseFilter wraps the auto generated filter.Parse function. +// It parses the given filter string and returns on failure the original parser error, +// which causes the parser to fail. +func ParseFilter(expr string, opts ...Option) (Filter, error) { + filter, err := Parse("", []byte(expr), opts...) + if err != nil { + parserErr := err.(errList)[0].(*parserError) + return nil, fmt.Errorf("invalid filter '%s', %s", expr, parserErr.Inner) + } + + return filter.(Filter), nil } -// Parse parses an object filter expression. -func Parse(expression string) (Filter, error) { - parser := &Parser{tag: expression, length: len(expression)} - if parser.length == 0 { - return &All{}, nil +func fromChains(chain Filter, operator string, rules []interface{}) (Filter, error) { + newChain, err := NewChain(chain, operator) + if err != nil { + return nil, err + } + + for _, rule := range rules { + newChain.(Chainable).Add(false, rule.(Filter)) } - return parser.readFilter(0, "", nil) + return newChain, nil +} + +// ensureNegateOperator panics if the given operator isn't the logical negate operator. +func ensureNegateOperator(op string, pos int) { + if op != "!" { + panic(fmt.Sprintf("unexpected logical operator %q at pos %d", op, pos)) + } } -// readFilter reads the entire filter from the Parser.tag and derives a filter.Filter from it. -// Returns an error on parsing failure. -func (p *Parser) readFilter(nestingLevel int, operator string, rules []Filter) (Filter, error) { - negate := false - for p.pos < p.length { - condition, err := p.readCondition() +func createFromRules(c *current, chain Chainable, rules []interface{}) (Filter, error) { + operator := c.globalStore["operator"].(string) + op := rules[0].(string) + filters := rules[1].([]interface{}) + if operator == "|" && (op == "!" || op == "&") { + lastRule := chain.Rules()[len(chain.Rules())-1] + chain.Add(true, chain.Rules()[:len(chain.Rules())-1]...) + + newChain, err := fromChains(lastRule, op, filters) if err != nil { return nil, err } - next := p.readChar() - if condition == nil { - if next == "!" { - negate = true - continue - } + filters = []interface{}{newChain} + } else { + operator = op + } - if operator == "" && len(rules) > 0 && (next == "&" || next == "|") { - operator = next - continue - } + c.globalStore["operator"] = op - if next == "" { - break - } + return fromChains(chain.(Filter), operator, filters) +} - if next == ")" { - p.openParenthesis-- +var g = &grammar{ + rules: []*rule{ + { + name: "FilterRule", + pos: position{line: 61, col: 1, offset: 1754}, + expr: &choiceExpr{ + pos: position{line: 61, col: 15, offset: 1768}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 61, col: 15, offset: 1768}, + run: (*parser).callonFilterRule2, + expr: &seqExpr{ + pos: position{line: 61, col: 15, offset: 1768}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 61, col: 15, offset: 1768}, + label: "group", + expr: &ruleRefExpr{ + pos: position{line: 61, col: 21, offset: 1774}, + name: "FilterRuleGroups", + }, + }, + &ruleRefExpr{ + pos: position{line: 61, col: 38, offset: 1791}, + name: "EOF", + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 64, col: 5, offset: 1827}, + run: (*parser).callonFilterRule7, + expr: &seqExpr{ + pos: position{line: 64, col: 5, offset: 1827}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 64, col: 5, offset: 1827}, + name: "FilterRuleGroups", + }, + &litMatcher{ + pos: position{line: 64, col: 22, offset: 1844}, + val: ")", + ignoreCase: false, + want: "\")\"", + }, + &ruleRefExpr{ + pos: position{line: 64, col: 26, offset: 1848}, + name: "EOF", + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 67, col: 5, offset: 1930}, + run: (*parser).callonFilterRule12, + expr: &seqExpr{ + pos: position{line: 67, col: 5, offset: 1930}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 67, col: 5, offset: 1930}, + label: "group1", + expr: &ruleRefExpr{ + pos: position{line: 67, col: 12, offset: 1937}, + name: "FilterRuleGroups", + }, + }, + &labeledExpr{ + pos: position{line: 67, col: 29, offset: 1954}, + label: "group2", + expr: &ruleRefExpr{ + pos: position{line: 67, col: 36, offset: 1961}, + name: "FilterRuleGroups", + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "FilterRuleGroups", + pos: position{line: 70, col: 1, offset: 2072}, + expr: &choiceExpr{ + pos: position{line: 70, col: 21, offset: 2092}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 70, col: 21, offset: 2092}, + run: (*parser).callonFilterRuleGroups2, + expr: &seqExpr{ + pos: position{line: 70, col: 21, offset: 2092}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 70, col: 21, offset: 2092}, + label: "optionalLop", + expr: &zeroOrOneExpr{ + pos: position{line: 70, col: 33, offset: 2104}, + expr: &ruleRefExpr{ + pos: position{line: 70, col: 33, offset: 2104}, + name: "LogicalOperatorExpr", + }, + }, + }, + &labeledExpr{ + pos: position{line: 70, col: 54, offset: 2125}, + label: "group", + expr: &ruleRefExpr{ + pos: position{line: 70, col: 60, offset: 2131}, + name: "FilterRuleGroup", + }, + }, + &labeledExpr{ + pos: position{line: 70, col: 76, offset: 2147}, + label: "lop", + expr: &ruleRefExpr{ + pos: position{line: 70, col: 80, offset: 2151}, + name: "LogicalOperatorExpr", + }, + }, + &labeledExpr{ + pos: position{line: 70, col: 100, offset: 2171}, + label: "group1", + expr: &oneOrMoreExpr{ + pos: position{line: 70, col: 107, offset: 2178}, + expr: &ruleRefExpr{ + pos: position{line: 70, col: 107, offset: 2178}, + name: "FilterRuleGroup", + }, + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 96, col: 5, offset: 3006}, + run: (*parser).callonFilterRuleGroups14, + expr: &seqExpr{ + pos: position{line: 96, col: 5, offset: 3006}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 96, col: 5, offset: 3006}, + label: "lop", + expr: &zeroOrOneExpr{ + pos: position{line: 96, col: 9, offset: 3010}, + expr: &ruleRefExpr{ + pos: position{line: 96, col: 9, offset: 3010}, + name: "LogicalOperatorExpr", + }, + }, + }, + &labeledExpr{ + pos: position{line: 96, col: 30, offset: 3031}, + label: "group", + expr: &ruleRefExpr{ + pos: position{line: 96, col: 36, offset: 3037}, + name: "FilterRuleGroup", + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "FilterRuleGroup", + pos: position{line: 105, col: 1, offset: 3208}, + expr: &choiceExpr{ + pos: position{line: 105, col: 20, offset: 3227}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 105, col: 20, offset: 3227}, + run: (*parser).callonFilterRuleGroup2, + expr: &seqExpr{ + pos: position{line: 105, col: 20, offset: 3227}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 105, col: 20, offset: 3227}, + label: "filter", + expr: &ruleRefExpr{ + pos: position{line: 105, col: 27, offset: 3234}, + name: "Filter", + }, + }, + &labeledExpr{ + pos: position{line: 105, col: 34, offset: 3241}, + label: "firstLop", + expr: &ruleRefExpr{ + pos: position{line: 105, col: 43, offset: 3250}, + name: "LogicalOperatorExpr", + }, + }, + &litMatcher{ + pos: position{line: 105, col: 63, offset: 3270}, + val: "(", + ignoreCase: false, + want: "\"(\"", + }, + &labeledExpr{ + pos: position{line: 105, col: 67, offset: 3274}, + label: "lop", + expr: &zeroOrOneExpr{ + pos: position{line: 105, col: 71, offset: 3278}, + expr: &ruleRefExpr{ + pos: position{line: 105, col: 71, offset: 3278}, + name: "LogicalOperatorExpr", + }, + }, + }, + &labeledExpr{ + pos: position{line: 105, col: 92, offset: 3299}, + label: "group", + expr: &ruleRefExpr{ + pos: position{line: 105, col: 98, offset: 3305}, + name: "FilterRuleGroups", + }, + }, + &litMatcher{ + pos: position{line: 105, col: 115, offset: 3322}, + val: ")", + ignoreCase: false, + want: "\")\"", + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 130, col: 5, offset: 4008}, + run: (*parser).callonFilterRuleGroup15, + expr: &seqExpr{ + pos: position{line: 130, col: 5, offset: 4008}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 130, col: 5, offset: 4008}, + val: "(", + ignoreCase: false, + want: "\"(\"", + }, + &labeledExpr{ + pos: position{line: 130, col: 9, offset: 4012}, + label: "lop", + expr: &zeroOrOneExpr{ + pos: position{line: 130, col: 13, offset: 4016}, + expr: &ruleRefExpr{ + pos: position{line: 130, col: 13, offset: 4016}, + name: "LogicalOperatorExpr", + }, + }, + }, + &labeledExpr{ + pos: position{line: 130, col: 34, offset: 4037}, + label: "group", + expr: &ruleRefExpr{ + pos: position{line: 130, col: 40, offset: 4043}, + name: "FilterRuleGroups", + }, + }, + &litMatcher{ + pos: position{line: 130, col: 57, offset: 4060}, + val: ")", + ignoreCase: false, + want: "\")\"", + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 143, col: 5, offset: 4395}, + run: (*parser).callonFilterRuleGroup24, + expr: &seqExpr{ + pos: position{line: 143, col: 5, offset: 4395}, + exprs: []interface{}{ + &zeroOrOneExpr{ + pos: position{line: 143, col: 5, offset: 4395}, + expr: &litMatcher{ + pos: position{line: 143, col: 5, offset: 4395}, + val: "!", + ignoreCase: false, + want: "\"!\"", + }, + }, + &litMatcher{ + pos: position{line: 143, col: 10, offset: 4400}, + val: "()", + ignoreCase: false, + want: "\"()\"", + }, + }, + }, + }, + &seqExpr{ + pos: position{line: 146, col: 5, offset: 4498}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 146, col: 5, offset: 4498}, + label: "begin", + expr: &oneOrMoreExpr{ + pos: position{line: 146, col: 11, offset: 4504}, + expr: &seqExpr{ + pos: position{line: 146, col: 12, offset: 4505}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 146, col: 12, offset: 4505}, + val: "(", + ignoreCase: false, + want: "\"(\"", + }, + &zeroOrOneExpr{ + pos: position{line: 146, col: 16, offset: 4509}, + expr: &ruleRefExpr{ + pos: position{line: 146, col: 16, offset: 4509}, + name: "LogicalOperatorExpr", + }, + }, + }, + }, + }, + }, + &andCodeExpr{ + pos: position{line: 146, col: 39, offset: 4532}, + run: (*parser).callonFilterRuleGroup36, + }, + }, + }, + &actionExpr{ + pos: position{line: 164, col: 5, offset: 4955}, + run: (*parser).callonFilterRuleGroup37, + expr: &labeledExpr{ + pos: position{line: 164, col: 5, offset: 4955}, + label: "filter", + expr: &ruleRefExpr{ + pos: position{line: 164, col: 12, offset: 4962}, + name: "Filter", + }, + }, + }, + }, + }, + }, + { + name: "Filter", + pos: position{line: 167, col: 1, offset: 4998}, + expr: &choiceExpr{ + pos: position{line: 167, col: 11, offset: 5008}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 167, col: 11, offset: 5008}, + run: (*parser).callonFilter2, + expr: &seqExpr{ + pos: position{line: 167, col: 11, offset: 5008}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 167, col: 11, offset: 5008}, + label: "chain", + expr: &ruleRefExpr{ + pos: position{line: 167, col: 17, offset: 5014}, + name: "FilterChains", + }, + }, + &labeledExpr{ + pos: position{line: 167, col: 30, offset: 5027}, + label: "lop", + expr: &ruleRefExpr{ + pos: position{line: 167, col: 34, offset: 5031}, + name: "LogicalOperatorExpr", + }, + }, + &labeledExpr{ + pos: position{line: 167, col: 54, offset: 5051}, + label: "chains", + expr: &oneOrMoreExpr{ + pos: position{line: 167, col: 61, offset: 5058}, + expr: &ruleRefExpr{ + pos: position{line: 167, col: 61, offset: 5058}, + name: "FilterChains", + }, + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 170, col: 5, offset: 5158}, + run: (*parser).callonFilter11, + expr: &labeledExpr{ + pos: position{line: 170, col: 5, offset: 5158}, + label: "chain", + expr: &ruleRefExpr{ + pos: position{line: 170, col: 11, offset: 5164}, + name: "FilterChains", + }, + }, + }, + }, + }, + }, + { + name: "FilterChains", + pos: position{line: 173, col: 1, offset: 5205}, + expr: &actionExpr{ + pos: position{line: 173, col: 17, offset: 5221}, + run: (*parser).callonFilterChains1, + expr: &seqExpr{ + pos: position{line: 173, col: 17, offset: 5221}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 173, col: 17, offset: 5221}, + label: "rule", + expr: &ruleRefExpr{ + pos: position{line: 173, col: 22, offset: 5226}, + name: "FilterChain", + }, + }, + &labeledExpr{ + pos: position{line: 173, col: 34, offset: 5238}, + label: "chains", + expr: &zeroOrOneExpr{ + pos: position{line: 173, col: 41, offset: 5245}, + expr: &seqExpr{ + pos: position{line: 173, col: 42, offset: 5246}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 173, col: 42, offset: 5246}, + name: "LogicalOperatorExpr", + }, + &oneOrMoreExpr{ + pos: position{line: 173, col: 62, offset: 5266}, + expr: &ruleRefExpr{ + pos: position{line: 173, col: 62, offset: 5266}, + name: "ConditionExpr", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "FilterChain", + pos: position{line: 180, col: 1, offset: 5416}, + expr: &choiceExpr{ + pos: position{line: 180, col: 16, offset: 5431}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 180, col: 16, offset: 5431}, + run: (*parser).callonFilterChain2, + expr: &seqExpr{ + pos: position{line: 180, col: 16, offset: 5431}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 180, col: 16, offset: 5431}, + label: "cond", + expr: &ruleRefExpr{ + pos: position{line: 180, col: 21, offset: 5436}, + name: "ConditionExpr", + }, + }, + &labeledExpr{ + pos: position{line: 180, col: 35, offset: 5450}, + label: "conds", + expr: &zeroOrOneExpr{ + pos: position{line: 180, col: 41, offset: 5456}, + expr: &seqExpr{ + pos: position{line: 180, col: 42, offset: 5457}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 180, col: 42, offset: 5457}, + name: "LogicalOperatorExpr", + }, + &labeledExpr{ + pos: position{line: 180, col: 62, offset: 5477}, + label: "cond", + expr: &ruleRefExpr{ + pos: position{line: 180, col: 67, offset: 5482}, + name: "ConditionExpr", + }, + }, + }, + }, + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 191, col: 5, offset: 5839}, + run: (*parser).callonFilterChain12, + expr: &labeledExpr{ + pos: position{line: 191, col: 5, offset: 5839}, + label: "lop", + expr: &ruleRefExpr{ + pos: position{line: 191, col: 9, offset: 5843}, + name: "LogicalOperatorExpr", + }, + }, + }, + }, + }, + }, + { + name: "ConditionExpr", + pos: position{line: 194, col: 1, offset: 5927}, + expr: &choiceExpr{ + pos: position{line: 194, col: 18, offset: 5944}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 194, col: 18, offset: 5944}, + run: (*parser).callonConditionExpr2, + expr: &seqExpr{ + pos: position{line: 194, col: 18, offset: 5944}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 194, col: 18, offset: 5944}, + label: "col", + expr: &ruleRefExpr{ + pos: position{line: 194, col: 22, offset: 5948}, + name: "Identifier", + }, + }, + &labeledExpr{ + pos: position{line: 194, col: 33, offset: 5959}, + label: "op", + expr: &ruleRefExpr{ + pos: position{line: 194, col: 36, offset: 5962}, + name: "OperatorExpr", + }, + }, + &labeledExpr{ + pos: position{line: 194, col: 49, offset: 5975}, + label: "val", + expr: &ruleRefExpr{ + pos: position{line: 194, col: 53, offset: 5979}, + name: "Identifier", + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 209, col: 5, offset: 6298}, + run: (*parser).callonConditionExpr10, + expr: &labeledExpr{ + pos: position{line: 209, col: 5, offset: 6298}, + label: "expr", + expr: &ruleRefExpr{ + pos: position{line: 209, col: 10, offset: 6303}, + name: "ExistsExpr", + }, + }, + }, + &actionExpr{ + pos: position{line: 212, col: 5, offset: 6345}, + run: (*parser).callonConditionExpr13, + expr: &labeledExpr{ + pos: position{line: 212, col: 5, offset: 6345}, + label: "op", + expr: &ruleRefExpr{ + pos: position{line: 212, col: 8, offset: 6348}, + name: "OperatorExpr", + }, + }, + }, + }, + }, + }, + { + name: "ExistsExpr", + pos: position{line: 215, col: 1, offset: 6425}, + expr: &actionExpr{ + pos: position{line: 215, col: 15, offset: 6439}, + run: (*parser).callonExistsExpr1, + expr: &labeledExpr{ + pos: position{line: 215, col: 15, offset: 6439}, + label: "col", + expr: &choiceExpr{ + pos: position{line: 215, col: 20, offset: 6444}, + alternatives: []interface{}{ + &seqExpr{ + pos: position{line: 215, col: 20, offset: 6444}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 215, col: 20, offset: 6444}, + name: "Identifier", + }, + ¬Expr{ + pos: position{line: 215, col: 31, offset: 6455}, + expr: &ruleRefExpr{ + pos: position{line: 215, col: 32, offset: 6456}, + name: "OperatorExpr", + }, + }, + ¬Expr{ + pos: position{line: 215, col: 45, offset: 6469}, + expr: &litMatcher{ + pos: position{line: 215, col: 46, offset: 6470}, + val: "(", + ignoreCase: false, + want: "\"(\"", + }, + }, + }, + }, + &seqExpr{ + pos: position{line: 215, col: 52, offset: 6476}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 215, col: 52, offset: 6476}, + label: "col", + expr: &ruleRefExpr{ + pos: position{line: 215, col: 56, offset: 6480}, + name: "Identifier", + }, + }, + &andExpr{ + pos: position{line: 215, col: 67, offset: 6491}, + expr: &ruleRefExpr{ + pos: position{line: 215, col: 68, offset: 6492}, + name: "EOF", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "OperatorExpr", + pos: position{line: 220, col: 1, offset: 6600}, + expr: &choiceExpr{ + pos: position{line: 220, col: 17, offset: 6616}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 220, col: 17, offset: 6616}, + run: (*parser).callonOperatorExpr2, + expr: &seqExpr{ + pos: position{line: 220, col: 17, offset: 6616}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 220, col: 17, offset: 6616}, + label: "op", + expr: &ruleRefExpr{ + pos: position{line: 220, col: 20, offset: 6619}, + name: "OperatorsExpr", + }, + }, + &andExpr{ + pos: position{line: 220, col: 34, offset: 6633}, + expr: &ruleRefExpr{ + pos: position{line: 220, col: 35, offset: 6634}, + name: "Identifier", + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 227, col: 5, offset: 6805}, + run: (*parser).callonOperatorExpr8, + expr: &seqExpr{ + pos: position{line: 227, col: 5, offset: 6805}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 227, col: 5, offset: 6805}, + label: "op", + expr: &ruleRefExpr{ + pos: position{line: 227, col: 8, offset: 6808}, + name: "OperatorsExpr", + }, + }, + &labeledExpr{ + pos: position{line: 227, col: 22, offset: 6822}, + label: "op1", + expr: &ruleRefExpr{ + pos: position{line: 227, col: 26, offset: 6826}, + name: "OperatorsExpr", + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 230, col: 5, offset: 6937}, + run: (*parser).callonOperatorExpr14, + expr: &seqExpr{ + pos: position{line: 230, col: 5, offset: 6937}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 230, col: 5, offset: 6937}, + label: "op", + expr: &ruleRefExpr{ + pos: position{line: 230, col: 8, offset: 6940}, + name: "OperatorsExpr", + }, + }, + &labeledExpr{ + pos: position{line: 230, col: 22, offset: 6954}, + label: "parenthesis", + expr: &choiceExpr{ + pos: position{line: 230, col: 35, offset: 6967}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 230, col: 35, offset: 6967}, + val: "(", + ignoreCase: false, + want: "\"(\"", + }, + &litMatcher{ + pos: position{line: 230, col: 41, offset: 6973}, + val: ")", + ignoreCase: false, + want: "\")\"", + }, + }, + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 233, col: 5, offset: 7079}, + run: (*parser).callonOperatorExpr22, + expr: &seqExpr{ + pos: position{line: 233, col: 5, offset: 7079}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 233, col: 5, offset: 7079}, + label: "op", + expr: &ruleRefExpr{ + pos: position{line: 233, col: 8, offset: 7082}, + name: "OperatorsExpr", + }, + }, + ¬Expr{ + pos: position{line: 233, col: 22, offset: 7096}, + expr: &ruleRefExpr{ + pos: position{line: 233, col: 23, offset: 7097}, + name: "Identifier", + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "OperatorsExpr", + pos: position{line: 236, col: 1, offset: 7207}, + expr: &choiceExpr{ + pos: position{line: 236, col: 18, offset: 7224}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 236, col: 18, offset: 7224}, + val: "<=", + ignoreCase: false, + want: "\"<=\"", + }, + &litMatcher{ + pos: position{line: 236, col: 25, offset: 7231}, + val: ">=", + ignoreCase: false, + want: "\">=\"", + }, + &litMatcher{ + pos: position{line: 236, col: 32, offset: 7238}, + val: "!=", + ignoreCase: false, + want: "\"!=\"", + }, + &litMatcher{ + pos: position{line: 236, col: 39, offset: 7245}, + val: "=", + ignoreCase: false, + want: "\"=\"", + }, + &litMatcher{ + pos: position{line: 236, col: 45, offset: 7251}, + val: "<", + ignoreCase: false, + want: "\"<\"", + }, + &litMatcher{ + pos: position{line: 236, col: 51, offset: 7257}, + val: ">", + ignoreCase: false, + want: "\">\"", + }, + }, + }, + }, + { + name: "LogicalOperatorExpr", + pos: position{line: 237, col: 1, offset: 7261}, + expr: &choiceExpr{ + pos: position{line: 237, col: 24, offset: 7284}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 237, col: 24, offset: 7284}, + run: (*parser).callonLogicalOperatorExpr2, + expr: &labeledExpr{ + pos: position{line: 237, col: 24, offset: 7284}, + label: "lop", + expr: &choiceExpr{ + pos: position{line: 237, col: 29, offset: 7289}, + alternatives: []interface{}{ + &seqExpr{ + pos: position{line: 237, col: 29, offset: 7289}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 237, col: 29, offset: 7289}, + name: "LogicalOperatorsExpr", + }, + &andExpr{ + pos: position{line: 237, col: 50, offset: 7310}, + expr: &litMatcher{ + pos: position{line: 237, col: 51, offset: 7311}, + val: ")", + ignoreCase: false, + want: "\")\"", + }, + }, + }, + }, + &seqExpr{ + pos: position{line: 237, col: 57, offset: 7317}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 237, col: 57, offset: 7317}, + name: "LogicalOperatorsExpr", + }, + &andExpr{ + pos: position{line: 237, col: 78, offset: 7338}, + expr: &ruleRefExpr{ + pos: position{line: 237, col: 79, offset: 7339}, + name: "EOF", + }, + }, + }, + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 247, col: 5, offset: 7586}, + run: (*parser).callonLogicalOperatorExpr13, + expr: &seqExpr{ + pos: position{line: 247, col: 5, offset: 7586}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 247, col: 5, offset: 7586}, + label: "lop", + expr: &ruleRefExpr{ + pos: position{line: 247, col: 9, offset: 7590}, + name: "LogicalOperatorsExpr", + }, + }, + &labeledExpr{ + pos: position{line: 247, col: 30, offset: 7611}, + label: "lop2", + expr: &ruleRefExpr{ + pos: position{line: 247, col: 35, offset: 7616}, + name: "LogicalOperatorsExpr", + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 250, col: 5, offset: 7746}, + run: (*parser).callonLogicalOperatorExpr19, + expr: &labeledExpr{ + pos: position{line: 250, col: 5, offset: 7746}, + label: "lop", + expr: &ruleRefExpr{ + pos: position{line: 250, col: 9, offset: 7750}, + name: "LogicalOperatorsExpr", + }, + }, + }, + }, + }, + }, + { + name: "LogicalOperatorsExpr", + pos: position{line: 253, col: 1, offset: 7806}, + expr: &actionExpr{ + pos: position{line: 253, col: 25, offset: 7830}, + run: (*parser).callonLogicalOperatorsExpr1, + expr: &choiceExpr{ + pos: position{line: 253, col: 27, offset: 7832}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 253, col: 27, offset: 7832}, + val: "!", + ignoreCase: false, + want: "\"!\"", + }, + &litMatcher{ + pos: position{line: 253, col: 33, offset: 7838}, + val: "&", + ignoreCase: false, + want: "\"&\"", + }, + &litMatcher{ + pos: position{line: 253, col: 39, offset: 7844}, + val: "|", + ignoreCase: false, + want: "\"|\"", + }, + }, + }, + }, + }, + { + name: "Identifier", + displayName: "\"column or value\"", + pos: position{line: 256, col: 1, offset: 7887}, + expr: &actionExpr{ + pos: position{line: 256, col: 33, offset: 7919}, + run: (*parser).callonIdentifier1, + expr: &oneOrMoreExpr{ + pos: position{line: 256, col: 33, offset: 7919}, + expr: &charClassMatcher{ + pos: position{line: 256, col: 33, offset: 7919}, + val: "[a-zA-Z0-9_%*]", + chars: []rune{'_', '%', '*'}, + ranges: []rune{'a', 'z', 'A', 'Z', '0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + { + name: "EOF", + pos: position{line: 259, col: 1, offset: 7972}, + expr: ¬Expr{ + pos: position{line: 259, col: 8, offset: 7979}, + expr: &anyMatcher{ + line: 259, col: 9, offset: 7980, + }, + }, + }, + }, +} - if nestingLevel > 0 { - next = p.nextChar() - if next != "" && next != "&" && next != "|" && next != ")" { - p.pos++ - return nil, p.parseError(next, "Expected logical operator") - } +func (c *current) onFilterRule2(group interface{}) (interface{}, error) { + return group, nil - break - } +} - return nil, p.parseError(next, "") - } +func (p *parser) callonFilterRule2() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRule2(stack["group"]) +} - if next == "(" { - if p.nextChar() == "&" || p.nextChar() == "|" { - // When a logical operator follows directly after the opening parenthesis "(", - // this can't be a valid expression. E.g. "!(&" - next = p.readChar() +func (c *current) onFilterRule7() (interface{}, error) { + panic(fmt.Sprintf("unexpected %q at pos %d", ")", len(c.text))) - return nil, p.parseError(next, "") - } +} - p.openParenthesis++ +func (p *parser) callonFilterRule7() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRule7() +} - op := "" - if negate { - op = "!" - } +func (c *current) onFilterRule12(group1, group2 interface{}) (interface{}, error) { + panic(fmt.Sprintf("missing logical operator at pos %d", c.globalStore["position"])) - rule, err := p.readFilter(nestingLevel+1, op, nil) - if err != nil { - return nil, err - } +} - rules = append(rules, rule) - negate = false - continue - } +func (p *parser) callonFilterRule12() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRule12(stack["group1"], stack["group2"]) +} - if next == operator { - continue - } +func (c *current) onFilterRuleGroups2(optionalLop, group, lop, group1 interface{}) (interface{}, error) { + rule := group.(Filter) + if optionalLop != nil { + // Don't allow to use such an expression "&(foo=test)" other than the negate operator. + ensureNegateOperator(optionalLop.(string), c.pos.col) - // When the current operator is a "!", the next one can't be a logical operator. - if operator != "!" && (next == "&" || next == "|") { - if operator == "&" { - if len(rules) > 1 { - rules = []Filter{&All{rules: rules}} - } - - operator = next - } else if operator == "|" || (operator == "!" && next == "&") { - // The last pushed filter chain - lastRule := rules[len(rules)-1] - // Erase it from our Rules slice - rules = rules[:len(rules)-1] - - rule, err := p.readFilter(nestingLevel+1, next, []Filter{lastRule}) - if err != nil { - return nil, err - } - - rules = append(rules, rule) - } + none, err := NewChain(rule, "!") + if err != nil { + return nil, err + } - continue - } + rule = none + } - return nil, p.parseError(next, fmt.Sprintf("operator level %d", nestingLevel)) - } else { - if negate { - negate = false - rules = append(rules, &None{rules: []Filter{condition}}) - } else { - rules = append(rules, condition) - } + _, found := c.globalStore["operator"] + _, grouped := c.globalStore["grouped"] + // Don't care about the op precedence when the chain was wrapped in a parentheses. + if chain, ok := rule.(Chainable); ok && found && !grouped { + rules := []interface{}{lop.(string)} + rules = append(rules, group1.([]interface{})) - if next == "" { - break - } + return createFromRules(c, chain, rules) + } - if next == ")" { - p.openParenthesis-- + return fromChains(rule, lop.(string), group1.([]interface{})) - if nestingLevel > 0 { - next = p.nextChar() - if next != "" && next != "&" && next != "|" && next != ")" { - p.pos++ - return nil, p.parseError(next, "Expected logical operator") - } +} - break - } +func (p *parser) callonFilterRuleGroups2() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRuleGroups2(stack["optionalLop"], stack["group"], stack["lop"], stack["group1"]) +} - return nil, p.parseError(next, "") - } +func (c *current) onFilterRuleGroups14(lop, group interface{}) (interface{}, error) { + if lop != nil { + ensureNegateOperator(lop.(string), c.pos.col) - if next == operator { - continue - } + return NewChain(group.(Filter), "!") + } - if next == "&" || next == "|" { - if operator == "" || operator == "&" { - if operator == "&" && len(rules) > 1 { - all := &All{rules: rules} - rules = []Filter{all} - } - - operator = next - } else if operator == "" || (operator == "!" && next == "&") { - // The last pushed filter chain - lastRule := rules[len(rules)-1] - // Erase it from our Rules slice - rules = rules[:len(rules)-1] - - rule, err := p.readFilter(nestingLevel+1, next, []Filter{lastRule}) - if err != nil { - return nil, err - } - - rules = append(rules, rule) - } + return group, nil - continue - } +} - return nil, p.parseError(next, "") - } +func (p *parser) callonFilterRuleGroups14() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRuleGroups14(stack["lop"], stack["group"]) +} + +func (c *current) onFilterRuleGroup2(filter, firstLop, lop, group interface{}) (interface{}, error) { + chain, err := NewChain(filter.(Filter), firstLop.(string)) + if err != nil { + return nil, err } - if nestingLevel == 0 && p.pos < p.length { - return nil, p.parseError(operator, "Did not read full filter") + if lop != nil { + // Don't allow to use such an expression "(&foo=test)" other than the negate operator. + ensureNegateOperator(lop.(string), c.globalStore["position"].(int)) + + not, err := NewChain(group.(Filter), lop.(string)) + if err != nil { + return nil, err + } + + group = not } - if nestingLevel == 0 && p.openParenthesis > 0 { - return nil, fmt.Errorf("invalid filter '%s', missing %d closing ')' at pos %d", p.tag, p.openParenthesis, p.pos) + // Cache the current operator, which might be needed later to ensure operator precedence. + c.globalStore["operator"] = firstLop.(string) + + chain.(Chainable).Add(false, group.(Filter)) + + return chain, nil + +} + +func (p *parser) callonFilterRuleGroup2() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRuleGroup2(stack["filter"], stack["firstLop"], stack["lop"], stack["group"]) +} + +func (c *current) onFilterRuleGroup15(lop, group interface{}) (interface{}, error) { + delete(c.globalStore, "operator") + c.globalStore["grouped"] = true + + if lop != nil { + // Don't allow to use such an expression "(&foo=test)" other than the negate operator. + ensureNegateOperator(lop.(string), c.pos.col+1) + + return NewChain(group.(Filter), "!") } - if nestingLevel == 0 && p.openParenthesis < 0 { - return nil, fmt.Errorf("invalid filter '%s', unexpected closing ')' at pos %d", p.tag, p.pos) + return group, nil + +} + +func (p *parser) callonFilterRuleGroup15() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRuleGroup15(stack["lop"], stack["group"]) +} + +func (c *current) onFilterRuleGroup24() (interface{}, error) { + panic(fmt.Sprintf("empty filter groups are not allowed at pos %d", c.pos.col)) + +} + +func (p *parser) callonFilterRuleGroup24() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRuleGroup24() +} + +func (c *current) onFilterRuleGroup36(begin interface{}) (bool, error) { + if strings.Count(string(c.text), ")") != c.pos.col { + panic("mismatching opening and closing parentheses") } - var chain Filter - switch operator { - case "&": - chain = &All{rules: rules} - case "|": - chain = &Any{rules: rules} - case "!": - chain = &None{rules: rules} - case "": - if nestingLevel == 0 && rules != nil { - // There is only one filter tag, no chain - return rules[0], nil - } + val, ok := c.globalStore["recursion"] + if !ok { + c.globalStore["recursion"] = 1 + } else { + c.globalStore["recursion"] = val.(int) + 1 + } - chain = &All{rules: rules} - default: - return nil, p.parseError(operator, "") + if c.globalStore["recursion"].(int) <= 5 { + return false, nil } + panic("too many opening parentheses") + +} + +func (p *parser) callonFilterRuleGroup36() (bool, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRuleGroup36(stack["begin"]) +} + +func (c *current) onFilterRuleGroup37(filter interface{}) (interface{}, error) { + return filter, nil + +} + +func (p *parser) callonFilterRuleGroup37() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterRuleGroup37(stack["filter"]) +} + +func (c *current) onFilter2(chain, lop, chains interface{}) (interface{}, error) { + return fromChains(chain.(Filter), lop.(string), chains.([]interface{})) + +} + +func (p *parser) callonFilter2() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilter2(stack["chain"], stack["lop"], stack["chains"]) +} + +func (c *current) onFilter11(chain interface{}) (interface{}, error) { return chain, nil + +} + +func (p *parser) callonFilter11() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilter11(stack["chain"]) +} + +func (c *current) onFilterChains1(rule, chains interface{}) (interface{}, error) { + if chains == nil { + return rule, nil + } + + return createFromRules(c, rule.(Chainable), chains.([]interface{})) + +} + +func (p *parser) callonFilterChains1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterChains1(stack["rule"], stack["chains"]) +} + +func (c *current) onFilterChain2(cond, conds interface{}) (interface{}, error) { + if conds == nil { + return cond, nil + } + + // Cache the current operator, which might be needed in (Rule: FilterChains) to ensure operator precedence. + lop := conds.([]interface{})[0].(string) + c.globalStore["operator"] = lop + + return fromChains(cond.(Filter), lop, []interface{}{conds.([]interface{})[1]}) + +} + +func (p *parser) callonFilterChain2() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterChain2(stack["cond"], stack["conds"]) } -// readCondition reads the next filter.Filter. -// returns nil if there is no char to read and an error on parsing failure. -func (p *Parser) readCondition() (Filter, error) { - column, err := p.readColumn() - if err != nil || column == "" { +func (c *current) onFilterChain12(lop interface{}) (interface{}, error) { + // panics when the rule doesn't match + return nil, nil + +} + +func (p *parser) callonFilterChain12() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFilterChain12(stack["lop"]) +} + +func (c *current) onConditionExpr2(col, op, val interface{}) (interface{}, error) { + column, err := url.QueryUnescape(col.(string)) + if err != nil { return nil, err } - operator := "" - if strings.Contains("=> 0 { + buf.WriteRune('\n') + } + buf.WriteString(err.Error()) + } + return buf.String() + } +} + +// parserError wraps an error with a prefix indicating the rule in which +// the error occurred. The original error is stored in the Inner field. +type parserError struct { + Inner error + pos position + prefix string + expected []string +} + +// Error returns the error message. +func (p *parserError) Error() string { + return p.prefix + ": " + p.Inner.Error() +} + +// newParser creates a parser with the specified input source and options. +func newParser(filename string, b []byte, opts ...Option) *parser { + stats := Stats{ + ChoiceAltCnt: make(map[string]map[string]int), + } + + p := &parser{ + filename: filename, + errs: new(errList), + data: b, + pt: savepoint{position: position{line: 1}}, + recover: true, + cur: current{ + globalStore: make(storeDict), + }, + maxFailPos: position{col: 1, line: 1}, + maxFailExpected: make([]string, 0, 20), + Stats: &stats, + // start rule is rule [0] unless an alternate entrypoint is specified + entrypoint: g.rules[0].name, + } + p.setOptions(opts) + + if p.maxExprCnt == 0 { + p.maxExprCnt = math.MaxUint64 + } + + return p +} + +// setOptions applies the options to the parser. +func (p *parser) setOptions(opts []Option) { + for _, opt := range opts { + opt(p) + } +} + +type resultTuple struct { + v interface{} + b bool + end savepoint +} + +const choiceNoMatch = -1 + +// Stats stores some statistics, gathered during parsing +type Stats struct { + // ExprCnt counts the number of expressions processed during parsing + // This value is compared to the maximum number of expressions allowed + // (set by the MaxExpressions option). + ExprCnt uint64 + + // ChoiceAltCnt is used to count for each ordered choice expression, + // which alternative is used how may times. + // These numbers allow to optimize the order of the ordered choice expression + // to increase the performance of the parser + // + // The outer key of ChoiceAltCnt is composed of the name of the rule as well + // as the line and the column of the ordered choice. + // The inner key of ChoiceAltCnt is the number (one-based) of the matching alternative. + // For each alternative the number of matches are counted. If an ordered choice does not + // match, a special counter is incremented. The name of this counter is set with + // the parser option Statistics. + // For an alternative to be included in ChoiceAltCnt, it has to match at least once. + ChoiceAltCnt map[string]map[string]int +} + +type parser struct { + filename string + pt savepoint + cur current + + data []byte + errs *errList + + depth int + recover bool + + // rules table, maps the rule identifier to the rule node + rules map[string]*rule + // variables stack, map of label to value + vstack []map[string]interface{} + // rule stack, allows identification of the current rule in errors + rstack []*rule + + // parse fail + maxFailPos position + maxFailExpected []string + maxFailInvertExpected bool + + // max number of expressions to be parsed + maxExprCnt uint64 + // entrypoint for the parser + entrypoint string + + allowInvalidUTF8 bool + + *Stats + + choiceNoMatch string + // recovery expression stack, keeps track of the currently available recovery expression, these are traversed in reverse + recoveryStack []map[string]interface{} +} + +// push a variable set on the vstack. +func (p *parser) pushV() { + if cap(p.vstack) == len(p.vstack) { + // create new empty slot in the stack + p.vstack = append(p.vstack, nil) + } else { + // slice to 1 more + p.vstack = p.vstack[:len(p.vstack)+1] + } + + // get the last args set + m := p.vstack[len(p.vstack)-1] + if m != nil && len(m) == 0 { + // empty map, all good + return + } + + m = make(map[string]interface{}) + p.vstack[len(p.vstack)-1] = m +} + +// pop a variable set from the vstack. +func (p *parser) popV() { + // if the map is not empty, clear it + m := p.vstack[len(p.vstack)-1] + if len(m) > 0 { + // GC that map + p.vstack[len(p.vstack)-1] = nil + } + p.vstack = p.vstack[:len(p.vstack)-1] +} + +// push a recovery expression with its labels to the recoveryStack +func (p *parser) pushRecovery(labels []string, expr interface{}) { + if cap(p.recoveryStack) == len(p.recoveryStack) { + // create new empty slot in the stack + p.recoveryStack = append(p.recoveryStack, nil) + } else { + // slice to 1 more + p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)+1] + } + + m := make(map[string]interface{}, len(labels)) + for _, fl := range labels { + m[fl] = expr + } + p.recoveryStack[len(p.recoveryStack)-1] = m +} + +// pop a recovery expression from the recoveryStack +func (p *parser) popRecovery() { + // GC that map + p.recoveryStack[len(p.recoveryStack)-1] = nil + + p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)-1] +} + +func (p *parser) addErr(err error) { + p.addErrAt(err, p.pt.position, []string{}) +} + +func (p *parser) addErrAt(err error, pos position, expected []string) { + var buf bytes.Buffer + if p.filename != "" { + buf.WriteString(p.filename) + } + if buf.Len() > 0 { + buf.WriteString(":") + } + buf.WriteString(fmt.Sprintf("%d:%d (%d)", pos.line, pos.col, pos.offset)) + if len(p.rstack) > 0 { + if buf.Len() > 0 { + buf.WriteString(": ") + } + rule := p.rstack[len(p.rstack)-1] + if rule.displayName != "" { + buf.WriteString("rule " + rule.displayName) + } else { + buf.WriteString("rule " + rule.name) + } + } + pe := &parserError{Inner: err, pos: pos, prefix: buf.String(), expected: expected} + p.errs.add(pe) +} + +func (p *parser) failAt(fail bool, pos position, want string) { + // process fail if parsing fails and not inverted or parsing succeeds and invert is set + if fail == p.maxFailInvertExpected { + if pos.offset < p.maxFailPos.offset { + return + } + + if pos.offset > p.maxFailPos.offset { + p.maxFailPos = pos + p.maxFailExpected = p.maxFailExpected[:0] + } + + if p.maxFailInvertExpected { + want = "!" + want + } + p.maxFailExpected = append(p.maxFailExpected, want) + } } -// createCondition creates a filter.Filter based on the given operator. -// returns nil when invalid operator is given. -func (p *Parser) createCondition(column string, operator string, value string) (Filter, error) { - column = strings.TrimSpace(column) - switch operator { - case "=": - if strings.Contains(value, "*") { - return &Like{column: column, value: value}, nil +// read advances the parser to the next rune. +func (p *parser) read() { + p.pt.offset += p.pt.w + rn, n := utf8.DecodeRune(p.data[p.pt.offset:]) + p.pt.rn = rn + p.pt.w = n + p.pt.col++ + if rn == '\n' { + p.pt.line++ + p.pt.col = 0 + } + + if rn == utf8.RuneError && n == 1 { // see utf8.DecodeRune + if !p.allowInvalidUTF8 { + p.addErr(errInvalidEncoding) } + } +} + +// restore parser position to the savepoint pt. +func (p *parser) restore(pt savepoint) { + if pt.offset == p.pt.offset { + return + } + p.pt = pt +} + +// get the slice of bytes from the savepoint start to the current position. +func (p *parser) sliceFrom(start savepoint) []byte { + return p.data[start.position.offset:p.pt.position.offset] +} + +func (p *parser) buildRulesTable(g *grammar) { + p.rules = make(map[string]*rule, len(g.rules)) + for _, r := range g.rules { + p.rules[r.name] = r + } +} + +func (p *parser) parse(g *grammar) (val interface{}, err error) { + if len(g.rules) == 0 { + p.addErr(errNoRule) + return nil, p.errs.err() + } + + // TODO : not super critical but this could be generated + p.buildRulesTable(g) + + if p.recover { + // panic can be used in action code to stop parsing immediately + // and return the panic as an error. + defer func() { + if e := recover(); e != nil { + val = nil + switch e := e.(type) { + case error: + p.addErr(e) + default: + p.addErr(fmt.Errorf("%v", e)) + } + err = p.errs.err() + } + }() + } - return &Equal{column: column, value: value}, nil - case "!=": - if strings.Contains(value, "*") { - return &Unlike{column: column, value: value}, nil + startRule, ok := p.rules[p.entrypoint] + if !ok { + p.addErr(errInvalidEntrypoint) + return nil, p.errs.err() + } + + p.read() // advance to first rune + val, ok = p.parseRule(startRule) + if !ok { + if len(*p.errs) == 0 { + // If parsing fails, but no errors have been recorded, the expected values + // for the farthest parser position are returned as error. + maxFailExpectedMap := make(map[string]struct{}, len(p.maxFailExpected)) + for _, v := range p.maxFailExpected { + maxFailExpectedMap[v] = struct{}{} + } + expected := make([]string, 0, len(maxFailExpectedMap)) + eof := false + if _, ok := maxFailExpectedMap["!."]; ok { + delete(maxFailExpectedMap, "!.") + eof = true + } + for k := range maxFailExpectedMap { + expected = append(expected, k) + } + sort.Strings(expected) + if eof { + expected = append(expected, "EOF") + } + p.addErrAt(errors.New("no match found, expected: "+listJoin(expected, ", ", "or")), p.maxFailPos, expected) } - return &UnEqual{column: column, value: value}, nil - case ">": - return &GreaterThan{column: column, value: value}, nil - case ">=": - return &GreaterThanOrEqual{column: column, value: value}, nil - case "<": - return &LessThan{column: column, value: value}, nil - case "<=": - return &LessThanOrEqual{column: column, value: value}, nil + return nil, p.errs.err() + } + return val, p.errs.err() +} + +func listJoin(list []string, sep string, lastSep string) string { + switch len(list) { + case 0: + return "" + case 1: + return list[0] default: - return nil, fmt.Errorf("invalid operator %s provided", operator) + return strings.Join(list[:len(list)-1], sep) + " " + lastSep + " " + list[len(list)-1] } } -// readColumn reads a column name from the Parser.tag. -// returns empty string if there is no char to read. -func (p *Parser) readColumn() (string, error) { - return url.QueryUnescape(p.readUntil("=()&|><") - if value == "" { - return "", nil +func (p *parser) parseExpr(expr interface{}) (interface{}, bool) { + + p.ExprCnt++ + if p.ExprCnt > p.maxExprCnt { + panic(errMaxExprCnt) } - return url.QueryUnescape(value) + var val interface{} + var ok bool + switch expr := expr.(type) { + case *actionExpr: + val, ok = p.parseActionExpr(expr) + case *andCodeExpr: + val, ok = p.parseAndCodeExpr(expr) + case *andExpr: + val, ok = p.parseAndExpr(expr) + case *anyMatcher: + val, ok = p.parseAnyMatcher(expr) + case *charClassMatcher: + val, ok = p.parseCharClassMatcher(expr) + case *choiceExpr: + val, ok = p.parseChoiceExpr(expr) + case *labeledExpr: + val, ok = p.parseLabeledExpr(expr) + case *litMatcher: + val, ok = p.parseLitMatcher(expr) + case *notCodeExpr: + val, ok = p.parseNotCodeExpr(expr) + case *notExpr: + val, ok = p.parseNotExpr(expr) + case *oneOrMoreExpr: + val, ok = p.parseOneOrMoreExpr(expr) + case *recoveryExpr: + val, ok = p.parseRecoveryExpr(expr) + case *ruleRefExpr: + val, ok = p.parseRuleRefExpr(expr) + case *seqExpr: + val, ok = p.parseSeqExpr(expr) + case *throwExpr: + val, ok = p.parseThrowExpr(expr) + case *zeroOrMoreExpr: + val, ok = p.parseZeroOrMoreExpr(expr) + case *zeroOrOneExpr: + val, ok = p.parseZeroOrOneExpr(expr) + default: + panic(fmt.Sprintf("unknown expression type %T", expr)) + } + return val, ok } -// readUntil reads chars until any of the given characters -// May return empty string if there is no char to read -func (p *Parser) readUntil(chars string) string { - var buffer string - for char := p.readChar(); char != ""; char = p.readChar() { - if strings.Contains(chars, char) { - p.pos-- - break +func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) { + start := p.pt + val, ok := p.parseExpr(act.expr) + if ok { + p.cur.pos = start.position + p.cur.text = p.sliceFrom(start) + actVal, err := act.run(p) + if err != nil { + p.addErrAt(err, start.position, []string{}) } - buffer += char + val = actVal } + return val, ok +} - return buffer +func (p *parser) parseAndCodeExpr(and *andCodeExpr) (interface{}, bool) { + + ok, err := and.run(p) + if err != nil { + p.addErr(err) + } + + return nil, ok } -// readChar peeks the next char of the Parser.tag and increments the Parser.pos by one -// returns empty if there is no char to read -func (p *Parser) readChar() string { - if p.pos < p.length { - pos := p.pos - p.pos++ +func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) { + pt := p.pt + p.pushV() + _, ok := p.parseExpr(and.expr) + p.popV() + p.restore(pt) - return string(p.tag[pos]) + return nil, ok +} + +func (p *parser) parseAnyMatcher(any *anyMatcher) (interface{}, bool) { + if p.pt.rn == utf8.RuneError && p.pt.w == 0 { + // EOF - see utf8.DecodeRune + p.failAt(false, p.pt.position, ".") + return nil, false } + start := p.pt + p.read() + p.failAt(true, start.position, ".") + return p.sliceFrom(start), true +} - return "" +func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool) { + cur := p.pt.rn + start := p.pt + + // can't match EOF + if cur == utf8.RuneError && p.pt.w == 0 { // see utf8.DecodeRune + p.failAt(false, start.position, chr.val) + return nil, false + } + + if chr.ignoreCase { + cur = unicode.ToLower(cur) + } + + // try to match in the list of available chars + for _, rn := range chr.chars { + if rn == cur { + if chr.inverted { + p.failAt(false, start.position, chr.val) + return nil, false + } + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + } + + // try to match in the list of ranges + for i := 0; i < len(chr.ranges); i += 2 { + if cur >= chr.ranges[i] && cur <= chr.ranges[i+1] { + if chr.inverted { + p.failAt(false, start.position, chr.val) + return nil, false + } + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + } + + // try to match in the list of Unicode classes + for _, cl := range chr.classes { + if unicode.Is(cl, cur) { + if chr.inverted { + p.failAt(false, start.position, chr.val) + return nil, false + } + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + } + + if chr.inverted { + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + p.failAt(false, start.position, chr.val) + return nil, false +} + +func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) { + for altI, alt := range ch.alternatives { + // dummy assignment to prevent compile error if optimized + _ = altI + + p.pushV() + val, ok := p.parseExpr(alt) + p.popV() + if ok { + return val, ok + } + } + return nil, false +} + +func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) { + p.pushV() + val, ok := p.parseExpr(lab.expr) + p.popV() + if ok && lab.label != "" { + m := p.vstack[len(p.vstack)-1] + m[lab.label] = val + } + return val, ok +} + +func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) { + start := p.pt + for _, want := range lit.val { + cur := p.pt.rn + if lit.ignoreCase { + cur = unicode.ToLower(cur) + } + if cur != want { + p.failAt(false, start.position, lit.want) + p.restore(start) + return nil, false + } + p.read() + } + p.failAt(true, start.position, lit.want) + return p.sliceFrom(start), true +} + +func (p *parser) parseNotCodeExpr(not *notCodeExpr) (interface{}, bool) { + ok, err := not.run(p) + if err != nil { + p.addErr(err) + } + + return nil, !ok } -// nextChar peeks the next char from the parser tag -// returns empty string if there is no char to read -func (p *Parser) nextChar() string { - if p.pos < p.length { - return string(p.tag[p.pos]) +func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) { + pt := p.pt + p.pushV() + p.maxFailInvertExpected = !p.maxFailInvertExpected + _, ok := p.parseExpr(not.expr) + p.maxFailInvertExpected = !p.maxFailInvertExpected + p.popV() + p.restore(pt) + + return nil, !ok +} + +func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (interface{}, bool) { + var vals []interface{} + + for { + p.pushV() + val, ok := p.parseExpr(expr.expr) + p.popV() + if !ok { + if len(vals) == 0 { + // did not match once, no match + return nil, false + } + return vals, true + } + vals = append(vals, val) } +} + +func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (interface{}, bool) { + + p.pushRecovery(recover.failureLabel, recover.recoverExpr) + val, ok := p.parseExpr(recover.expr) + p.popRecovery() - return "" + return val, ok } -// parseError returns a formatted and detailed parser error. -// If you don't provide the char that causes the parser to fail, the char at `p.pos` is automatically used. -// By specifying the `msg` arg you can provide additional err hints that can help debugging. -func (p *Parser) parseError(invalidChar string, msg string) error { - if invalidChar == "" { - pos := p.pos - if p.pos == p.length { - pos-- +func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (interface{}, bool) { + if ref.name == "" { + panic(fmt.Sprintf("%s: invalid rule: missing name", ref.pos)) + } + + rule := p.rules[ref.name] + if rule == nil { + p.addErr(fmt.Errorf("undefined rule: %s", ref.name)) + return nil, false + } + return p.parseRule(rule) +} + +func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) { + vals := make([]interface{}, 0, len(seq.exprs)) + + pt := p.pt + for _, expr := range seq.exprs { + val, ok := p.parseExpr(expr) + if !ok { + p.restore(pt) + return nil, false } + vals = append(vals, val) + } + return vals, true +} + +func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) { - invalidChar = string(p.tag[pos]) + for i := len(p.recoveryStack) - 1; i >= 0; i-- { + if recoverExpr, ok := p.recoveryStack[i][expr.label]; ok { + if val, ok := p.parseExpr(recoverExpr); ok { + return val, ok + } + } } - if msg != "" { - msg = ": " + msg + return nil, false +} + +func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (interface{}, bool) { + var vals []interface{} + + for { + p.pushV() + val, ok := p.parseExpr(expr.expr) + p.popV() + if !ok { + return vals, true + } + vals = append(vals, val) } +} - return fmt.Errorf("invalid filter '%s', unexpected %s at pos %d%s", p.tag, invalidChar, p.pos, msg) +func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (interface{}, bool) { + p.pushV() + val, _ := p.parseExpr(expr.expr) + p.popV() + // whether it matched or not, consider it a match + return val, true }