From 16a214416b4a875c7ccf78c1ed83d516e2dd2467 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Fri, 8 Sep 2023 10:21:25 +0200 Subject: [PATCH 01/11] enhancement: add more kql spec tests and simplify ast normalization --- services/search/pkg/query/kql/cast.go | 22 +- services/search/pkg/query/kql/dictionary.peg | 64 +- .../search/pkg/query/kql/dictionary_gen.go | 3556 +++++++---------- .../search/pkg/query/kql/dictionary_test.go | 361 +- services/search/pkg/query/kql/factory.go | 39 +- services/search/pkg/query/kql/kql.go | 120 +- services/search/pkg/query/kql/kql_test.go | 32 +- 7 files changed, 1523 insertions(+), 2671 deletions(-) diff --git a/services/search/pkg/query/kql/cast.go b/services/search/pkg/query/kql/cast.go index d9710d35ce1..41c8183a62a 100644 --- a/services/search/pkg/query/kql/cast.go +++ b/services/search/pkg/query/kql/cast.go @@ -4,8 +4,6 @@ import ( "fmt" "time" - "github.com/araddon/dateparse" - "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -23,24 +21,8 @@ func toNodes[T ast.Node](in interface{}) ([]T, error) { switch v := in.(type) { case []T: return v, nil - case T: - return []T{v}, nil - case []interface{}: - var ts []T - for _, inter := range v { - n, err := toNodes[T](inter) - if err != nil { - return nil, err - } - - ts = append(ts, n...) - } - return ts, nil - case nil: - return nil, nil default: - var t T - return nil, fmt.Errorf("can't convert '%T' to '%T'", in, t) + return nil, fmt.Errorf("can't convert '%T' to []ast.Node", in) } } @@ -74,5 +56,5 @@ func toTime(in interface{}) (time.Time, error) { return time.Time{}, err } - return dateparse.ParseLocal(ts) + return time.Parse(time.RFC3339Nano, ts) } diff --git a/services/search/pkg/query/kql/dictionary.peg b/services/search/pkg/query/kql/dictionary.peg index ca9658dfa53..6c177b2d87c 100644 --- a/services/search/pkg/query/kql/dictionary.peg +++ b/services/search/pkg/query/kql/dictionary.peg @@ -7,22 +7,19 @@ //////////////////////////////////////////////////////// AST <- - n:Nodes { - return buildAST(n, c.text, c.pos) + _ nodes:Nodes _ { + return buildAST(nodes, c.text, c.pos) } -//////////////////////////////////////////////////////// -// nodes -//////////////////////////////////////////////////////// - Nodes <- - (_ Node)+ - -Node <- - GroupNode / - PropertyRestrictionNodes / - OperatorBooleanNodes / - FreeTextKeywordNodes + _ head:( + GroupNode / + PropertyRestrictionNodes / + OperatorBooleanNode / + FreeTextKeywordNodes + ) _ tail:Nodes? { + return buildNodes(head, tail) + } //////////////////////////////////////////////////////// // nesting @@ -48,18 +45,7 @@ YesNoPropertyRestrictionNode <- } DateTimeRestrictionNode <- - k:Char+ o:( - OperatorGreaterOrEqualNode / - OperatorLessOrEqualNode / - OperatorGreaterNode / - OperatorLessNode / - OperatorEqualNode / - OperatorColonNode - ) '"'? v:( - DateTime / - FullDate / - FullTime - ) '"'? { + k:Char+ o:(OperatorGreaterOrEqualNode / OperatorLessOrEqualNode / OperatorGreaterNode / OperatorLessNode / OperatorEqualNode / OperatorColonNode) '"'? v:(FullDate "T" FullTime) '"'? { return buildDateTimeNode(k, o, v, c.text, c.pos) } @@ -90,23 +76,8 @@ WordNode <- // operators //////////////////////////////////////////////////////// -OperatorBooleanNodes <- - OperatorBooleanAndNode / - OperatorBooleanNotNode / - OperatorBooleanOrNode - -OperatorBooleanAndNode <- - ("AND" / "+") { - return buildOperatorNode(c.text, c.pos) - } - -OperatorBooleanNotNode <- - ("NOT" / "-") { - return buildOperatorNode(c.text, c.pos) - } - -OperatorBooleanOrNode <- - ("OR") { +OperatorBooleanNode <- + ("AND" / "OR" / "NOT" / "+" / "-") { return buildOperatorNode(c.text, c.pos) } @@ -185,11 +156,6 @@ FullTime <- return c.text, nil } -DateTime - = FullDate "T" FullTime { - return c.text, nil - } - //////////////////////////////////////////////////////// // misc //////////////////////////////////////////////////////// @@ -210,6 +176,4 @@ Digit <- } _ <- - [ \t]* { - return nil, nil - } + [ \t]* diff --git a/services/search/pkg/query/kql/dictionary_gen.go b/services/search/pkg/query/kql/dictionary_gen.go index afce6c5c85e..4c9e749ffbd 100644 --- a/services/search/pkg/query/kql/dictionary_gen.go +++ b/services/search/pkg/query/kql/dictionary_gen.go @@ -12,6 +12,7 @@ import ( "sort" "strconv" "strings" + "sync" "unicode" "unicode/utf8" ) @@ -24,1338 +25,79 @@ var g = &grammar{ expr: &actionExpr{ pos: position{line: 10, col: 5, offset: 154}, run: (*parser).callonAST1, - expr: &labeledExpr{ - pos: position{line: 10, col: 5, offset: 154}, - label: "n", - expr: &ruleRefExpr{ - pos: position{line: 10, col: 7, offset: 156}, - name: "Nodes", - }, - }, - }, - }, - { - name: "Nodes", - pos: position{line: 18, col: 1, offset: 337}, - expr: &oneOrMoreExpr{ - pos: position{line: 19, col: 5, offset: 350}, expr: &seqExpr{ - pos: position{line: 19, col: 6, offset: 351}, + pos: position{line: 10, col: 5, offset: 154}, exprs: []any{ - &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNodes3, - expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, - val: "[ \\t]", - chars: []rune{' ', '\t'}, - ignoreCase: false, - inverted: false, - }, + &ruleRefExpr{ + pos: position{line: 10, col: 5, offset: 154}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 10, col: 7, offset: 156}, + label: "nodes", + expr: &ruleRefExpr{ + pos: position{line: 10, col: 13, offset: 162}, + name: "Nodes", }, }, &ruleRefExpr{ - pos: position{line: 19, col: 8, offset: 353}, - name: "Node", + pos: position{line: 10, col: 19, offset: 168}, + name: "_", }, }, }, }, }, { - name: "Node", - pos: position{line: 21, col: 1, offset: 361}, - expr: &choiceExpr{ - pos: position{line: 22, col: 5, offset: 373}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 22, col: 5, offset: 373}, - name: "GroupNode", - }, - &actionExpr{ - pos: position{line: 46, col: 5, offset: 1042}, - run: (*parser).callonNode3, - expr: &seqExpr{ - pos: position{line: 46, col: 5, offset: 1042}, - exprs: []any{ - &labeledExpr{ - pos: position{line: 46, col: 5, offset: 1042}, - label: "k", - expr: &oneOrMoreExpr{ - pos: position{line: 46, col: 7, offset: 1044}, - expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, - run: (*parser).callonNode7, - expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, - val: "[A-Za-z]", - ranges: []rune{'A', 'Z', 'a', 'z'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - &choiceExpr{ - pos: position{line: 46, col: 14, offset: 1051}, - alternatives: []any{ - &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode10, - expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - }, - &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, - run: (*parser).callonNode12, - expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, - val: "=", - ignoreCase: false, - want: "\"=\"", - }, - }, - }, - }, - &labeledExpr{ - pos: position{line: 46, col: 53, offset: 1090}, - label: "v", - expr: &choiceExpr{ - pos: position{line: 46, col: 56, offset: 1093}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 46, col: 56, offset: 1093}, - val: "true", - ignoreCase: false, - want: "\"true\"", - }, - &litMatcher{ - pos: position{line: 46, col: 65, offset: 1102}, - val: "false", - ignoreCase: false, - want: "\"false\"", - }, - }, - }, - }, - }, + name: "Nodes", + pos: position{line: 14, col: 1, offset: 225}, + expr: &actionExpr{ + pos: position{line: 15, col: 5, offset: 238}, + run: (*parser).callonNodes1, + expr: &seqExpr{ + pos: position{line: 15, col: 5, offset: 238}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 15, col: 5, offset: 238}, + name: "_", }, - }, - &actionExpr{ - pos: position{line: 51, col: 5, offset: 1203}, - run: (*parser).callonNode18, - expr: &seqExpr{ - pos: position{line: 51, col: 5, offset: 1203}, - exprs: []any{ - &labeledExpr{ - pos: position{line: 51, col: 5, offset: 1203}, - label: "k", - expr: &oneOrMoreExpr{ - pos: position{line: 51, col: 7, offset: 1205}, - expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, - run: (*parser).callonNode22, - expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, - val: "[A-Za-z]", - ranges: []rune{'A', 'Z', 'a', 'z'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - &labeledExpr{ - pos: position{line: 51, col: 13, offset: 1211}, - label: "o", - expr: &choiceExpr{ - pos: position{line: 52, col: 9, offset: 1223}, - alternatives: []any{ - &actionExpr{ - pos: position{line: 139, col: 5, offset: 3168}, - run: (*parser).callonNode26, - expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3168}, - val: ">=", - ignoreCase: false, - want: "\">=\"", - }, - }, - &actionExpr{ - pos: position{line: 129, col: 5, offset: 2984}, - run: (*parser).callonNode28, - expr: &litMatcher{ - pos: position{line: 129, col: 5, offset: 2984}, - val: "<=", - ignoreCase: false, - want: "\"<=\"", - }, - }, - &actionExpr{ - pos: position{line: 134, col: 5, offset: 3073}, - run: (*parser).callonNode30, - expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3073}, - val: ">", - ignoreCase: false, - want: "\">\"", - }, - }, - &actionExpr{ - pos: position{line: 124, col: 5, offset: 2892}, - run: (*parser).callonNode32, - expr: &litMatcher{ - pos: position{line: 124, col: 5, offset: 2892}, - val: "<", - ignoreCase: false, - want: "\"<\"", - }, - }, - &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, - run: (*parser).callonNode34, - expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, - val: "=", - ignoreCase: false, - want: "\"=\"", - }, - }, - &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode36, - expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - }, - }, - }, - }, - &zeroOrOneExpr{ - pos: position{line: 58, col: 7, offset: 1403}, - expr: &litMatcher{ - pos: position{line: 58, col: 7, offset: 1403}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", - }, - }, - &labeledExpr{ - pos: position{line: 58, col: 12, offset: 1408}, - label: "v", - expr: &choiceExpr{ - pos: position{line: 59, col: 9, offset: 1420}, - alternatives: []any{ - &actionExpr{ - pos: position{line: 189, col: 5, offset: 4003}, - run: (*parser).callonNode42, - expr: &seqExpr{ - pos: position{line: 189, col: 5, offset: 4003}, - exprs: []any{ - &actionExpr{ - pos: position{line: 179, col: 5, offset: 3770}, - run: (*parser).callonNode44, - expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 3770}, - exprs: []any{ - &actionExpr{ - pos: position{line: 149, col: 5, offset: 3370}, - run: (*parser).callonNode46, - expr: &seqExpr{ - pos: position{line: 149, col: 5, offset: 3370}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode48, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode50, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode52, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode54, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 179, col: 14, offset: 3779}, - val: "-", - ignoreCase: false, - want: "\"-\"", - }, - &actionExpr{ - pos: position{line: 154, col: 5, offset: 3447}, - run: (*parser).callonNode57, - expr: &seqExpr{ - pos: position{line: 154, col: 5, offset: 3447}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode59, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode61, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 179, col: 28, offset: 3793}, - val: "-", - ignoreCase: false, - want: "\"-\"", - }, - &actionExpr{ - pos: position{line: 159, col: 5, offset: 3510}, - run: (*parser).callonNode64, - expr: &seqExpr{ - pos: position{line: 159, col: 5, offset: 3510}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode66, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode68, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 189, col: 14, offset: 4012}, - val: "T", - ignoreCase: false, - want: "\"T\"", - }, - &actionExpr{ - pos: position{line: 184, col: 5, offset: 3857}, - run: (*parser).callonNode71, - expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 3857}, - exprs: []any{ - &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, - run: (*parser).callonNode73, - expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode75, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode77, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 184, col: 14, offset: 3866}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, - run: (*parser).callonNode80, - expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode82, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode84, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 184, col: 29, offset: 3881}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - &actionExpr{ - pos: position{line: 174, col: 5, offset: 3706}, - run: (*parser).callonNode87, - expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 3706}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode89, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode91, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &zeroOrOneExpr{ - pos: position{line: 184, col: 44, offset: 3896}, - expr: &seqExpr{ - pos: position{line: 184, col: 45, offset: 3897}, - exprs: []any{ - &litMatcher{ - pos: position{line: 184, col: 45, offset: 3897}, - val: ".", - ignoreCase: false, - want: "\".\"", - }, - &oneOrMoreExpr{ - pos: position{line: 184, col: 49, offset: 3901}, - expr: &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode97, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - }, - &choiceExpr{ - pos: position{line: 184, col: 59, offset: 3911}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 184, col: 59, offset: 3911}, - val: "Z", - ignoreCase: false, - want: "\"Z\"", - }, - &seqExpr{ - pos: position{line: 184, col: 65, offset: 3917}, - exprs: []any{ - &charClassMatcher{ - pos: position{line: 184, col: 66, offset: 3918}, - val: "[+-]", - chars: []rune{'+', '-'}, - ignoreCase: false, - inverted: false, - }, - &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, - run: (*parser).callonNode103, - expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode105, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode107, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 184, col: 86, offset: 3938}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, - run: (*parser).callonNode110, - expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode112, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode114, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - &actionExpr{ - pos: position{line: 179, col: 5, offset: 3770}, - run: (*parser).callonNode116, - expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 3770}, - exprs: []any{ - &actionExpr{ - pos: position{line: 149, col: 5, offset: 3370}, - run: (*parser).callonNode118, - expr: &seqExpr{ - pos: position{line: 149, col: 5, offset: 3370}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode120, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode122, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode124, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode126, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 179, col: 14, offset: 3779}, - val: "-", - ignoreCase: false, - want: "\"-\"", - }, - &actionExpr{ - pos: position{line: 154, col: 5, offset: 3447}, - run: (*parser).callonNode129, - expr: &seqExpr{ - pos: position{line: 154, col: 5, offset: 3447}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode131, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode133, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 179, col: 28, offset: 3793}, - val: "-", - ignoreCase: false, - want: "\"-\"", - }, - &actionExpr{ - pos: position{line: 159, col: 5, offset: 3510}, - run: (*parser).callonNode136, - expr: &seqExpr{ - pos: position{line: 159, col: 5, offset: 3510}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode138, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode140, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - }, - }, - }, - &actionExpr{ - pos: position{line: 184, col: 5, offset: 3857}, - run: (*parser).callonNode142, - expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 3857}, - exprs: []any{ - &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, - run: (*parser).callonNode144, - expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode146, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode148, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 184, col: 14, offset: 3866}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, - run: (*parser).callonNode151, - expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode153, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode155, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 184, col: 29, offset: 3881}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - &actionExpr{ - pos: position{line: 174, col: 5, offset: 3706}, - run: (*parser).callonNode158, - expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 3706}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode160, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode162, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &zeroOrOneExpr{ - pos: position{line: 184, col: 44, offset: 3896}, - expr: &seqExpr{ - pos: position{line: 184, col: 45, offset: 3897}, - exprs: []any{ - &litMatcher{ - pos: position{line: 184, col: 45, offset: 3897}, - val: ".", - ignoreCase: false, - want: "\".\"", - }, - &oneOrMoreExpr{ - pos: position{line: 184, col: 49, offset: 3901}, - expr: &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode168, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - }, - &choiceExpr{ - pos: position{line: 184, col: 59, offset: 3911}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 184, col: 59, offset: 3911}, - val: "Z", - ignoreCase: false, - want: "\"Z\"", - }, - &seqExpr{ - pos: position{line: 184, col: 65, offset: 3917}, - exprs: []any{ - &charClassMatcher{ - pos: position{line: 184, col: 66, offset: 3918}, - val: "[+-]", - chars: []rune{'+', '-'}, - ignoreCase: false, - inverted: false, - }, - &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, - run: (*parser).callonNode174, - expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode176, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode178, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - &litMatcher{ - pos: position{line: 184, col: 86, offset: 3938}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, - run: (*parser).callonNode181, - expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, - exprs: []any{ - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode183, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, - run: (*parser).callonNode185, - expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - &zeroOrOneExpr{ - pos: position{line: 62, col: 7, offset: 1473}, - expr: &litMatcher{ - pos: position{line: 62, col: 7, offset: 1473}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", + &labeledExpr{ + pos: position{line: 15, col: 7, offset: 240}, + label: "head", + expr: &choiceExpr{ + pos: position{line: 16, col: 9, offset: 255}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 16, col: 9, offset: 255}, + name: "GroupNode", }, - }, - }, - }, - }, - &actionExpr{ - pos: position{line: 67, col: 5, offset: 1579}, - run: (*parser).callonNode189, - expr: &seqExpr{ - pos: position{line: 67, col: 5, offset: 1579}, - exprs: []any{ - &labeledExpr{ - pos: position{line: 67, col: 5, offset: 1579}, - label: "k", - expr: &oneOrMoreExpr{ - pos: position{line: 67, col: 7, offset: 1581}, - expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, - run: (*parser).callonNode193, - expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, - val: "[A-Za-z]", - ranges: []rune{'A', 'Z', 'a', 'z'}, - ignoreCase: false, - inverted: false, - }, - }, + &ruleRefExpr{ + pos: position{line: 17, col: 9, offset: 275}, + name: "PropertyRestrictionNodes", }, - }, - &choiceExpr{ - pos: position{line: 67, col: 14, offset: 1588}, - alternatives: []any{ - &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode196, - expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - }, - &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, - run: (*parser).callonNode198, - expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, - val: "=", - ignoreCase: false, - want: "\"=\"", - }, - }, + &ruleRefExpr{ + pos: position{line: 18, col: 9, offset: 310}, + name: "OperatorBooleanNode", }, - }, - &labeledExpr{ - pos: position{line: 67, col: 53, offset: 1627}, - label: "v", - expr: &choiceExpr{ - pos: position{line: 67, col: 56, offset: 1630}, - alternatives: []any{ - &actionExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - run: (*parser).callonNode202, - expr: &seqExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - exprs: []any{ - &litMatcher{ - pos: position{line: 203, col: 5, offset: 4253}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", - }, - &labeledExpr{ - pos: position{line: 203, col: 9, offset: 4257}, - label: "v", - expr: &zeroOrMoreExpr{ - pos: position{line: 203, col: 11, offset: 4259}, - expr: &charClassMatcher{ - pos: position{line: 203, col: 11, offset: 4259}, - val: "[^\"]", - chars: []rune{'"'}, - ignoreCase: false, - inverted: true, - }, - }, - }, - &litMatcher{ - pos: position{line: 203, col: 17, offset: 4265}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", - }, - }, - }, - }, - &oneOrMoreExpr{ - pos: position{line: 67, col: 65, offset: 1639}, - expr: &charClassMatcher{ - pos: position{line: 67, col: 65, offset: 1639}, - val: "[^ ()]", - chars: []rune{' ', '(', ')'}, - ignoreCase: false, - inverted: true, - }, - }, - }, + &ruleRefExpr{ + pos: position{line: 19, col: 9, offset: 340}, + name: "FreeTextKeywordNodes", }, }, }, }, - }, - &actionExpr{ - pos: position{line: 99, col: 5, offset: 2431}, - run: (*parser).callonNode211, - expr: &choiceExpr{ - pos: position{line: 99, col: 6, offset: 2432}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 99, col: 6, offset: 2432}, - val: "AND", - ignoreCase: false, - want: "\"AND\"", - }, - &litMatcher{ - pos: position{line: 99, col: 14, offset: 2440}, - val: "+", - ignoreCase: false, - want: "\"+\"", - }, - }, - }, - }, - &actionExpr{ - pos: position{line: 104, col: 5, offset: 2532}, - run: (*parser).callonNode215, - expr: &choiceExpr{ - pos: position{line: 104, col: 6, offset: 2533}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 104, col: 6, offset: 2533}, - val: "NOT", - ignoreCase: false, - want: "\"NOT\"", - }, - &litMatcher{ - pos: position{line: 104, col: 14, offset: 2541}, - val: "-", - ignoreCase: false, - want: "\"-\"", - }, - }, - }, - }, - &actionExpr{ - pos: position{line: 109, col: 5, offset: 2632}, - run: (*parser).callonNode219, - expr: &litMatcher{ - pos: position{line: 109, col: 6, offset: 2633}, - val: "OR", - ignoreCase: false, - want: "\"OR\"", - }, - }, - &actionExpr{ - pos: position{line: 80, col: 6, offset: 1919}, - run: (*parser).callonNode221, - expr: &seqExpr{ - pos: position{line: 80, col: 6, offset: 1919}, - exprs: []any{ - &zeroOrOneExpr{ - pos: position{line: 80, col: 6, offset: 1919}, - expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode224, - expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - }, - }, - &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode226, - expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, - val: "[ \\t]", - chars: []rune{' ', '\t'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - &labeledExpr{ - pos: position{line: 80, col: 27, offset: 1940}, - label: "v", - expr: &actionExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - run: (*parser).callonNode230, - expr: &seqExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - exprs: []any{ - &litMatcher{ - pos: position{line: 203, col: 5, offset: 4253}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", - }, - &labeledExpr{ - pos: position{line: 203, col: 9, offset: 4257}, - label: "v", - expr: &zeroOrMoreExpr{ - pos: position{line: 203, col: 11, offset: 4259}, - expr: &charClassMatcher{ - pos: position{line: 203, col: 11, offset: 4259}, - val: "[^\"]", - chars: []rune{'"'}, - ignoreCase: false, - inverted: true, - }, - }, - }, - &litMatcher{ - pos: position{line: 203, col: 17, offset: 4265}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", - }, - }, - }, - }, - }, - &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode237, - expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, - val: "[ \\t]", - chars: []rune{' ', '\t'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - &zeroOrOneExpr{ - pos: position{line: 80, col: 38, offset: 1951}, - expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode241, - expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - }, - }, - }, + &ruleRefExpr{ + pos: position{line: 20, col: 7, offset: 367}, + name: "_", }, - }, - &actionExpr{ - pos: position{line: 85, col: 6, offset: 2049}, - run: (*parser).callonNode243, - expr: &seqExpr{ - pos: position{line: 85, col: 6, offset: 2049}, - exprs: []any{ - &zeroOrOneExpr{ - pos: position{line: 85, col: 6, offset: 2049}, - expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode246, - expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - }, - }, - &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode248, - expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, - val: "[ \\t]", - chars: []rune{' ', '\t'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - &labeledExpr{ - pos: position{line: 85, col: 27, offset: 2070}, - label: "v", - expr: &oneOrMoreExpr{ - pos: position{line: 85, col: 29, offset: 2072}, - expr: &charClassMatcher{ - pos: position{line: 85, col: 29, offset: 2072}, - val: "[^ :()]", - chars: []rune{' ', ':', '(', ')'}, - ignoreCase: false, - inverted: true, - }, - }, - }, - &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode254, - expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, - val: "[ \\t]", - chars: []rune{' ', '\t'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - &zeroOrOneExpr{ - pos: position{line: 85, col: 40, offset: 2083}, - expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode258, - expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - }, + &labeledExpr{ + pos: position{line: 20, col: 9, offset: 369}, + label: "tail", + expr: &zeroOrOneExpr{ + pos: position{line: 20, col: 14, offset: 374}, + expr: &ruleRefExpr{ + pos: position{line: 20, col: 14, offset: 374}, + name: "Nodes", }, }, }, @@ -1365,78 +107,59 @@ var g = &grammar{ }, { name: "GroupNode", - pos: position{line: 31, col: 1, offset: 595}, + pos: position{line: 28, col: 1, offset: 552}, expr: &actionExpr{ - pos: position{line: 32, col: 5, offset: 612}, + pos: position{line: 29, col: 5, offset: 569}, run: (*parser).callonGroupNode1, expr: &seqExpr{ - pos: position{line: 32, col: 5, offset: 612}, + pos: position{line: 29, col: 5, offset: 569}, exprs: []any{ &labeledExpr{ - pos: position{line: 32, col: 5, offset: 612}, + pos: position{line: 29, col: 5, offset: 569}, label: "k", expr: &zeroOrOneExpr{ - pos: position{line: 32, col: 7, offset: 614}, + pos: position{line: 29, col: 7, offset: 571}, expr: &oneOrMoreExpr{ - pos: position{line: 32, col: 8, offset: 615}, - expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, - run: (*parser).callonGroupNode6, - expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, - val: "[A-Za-z]", - ranges: []rune{'A', 'Z', 'a', 'z'}, - ignoreCase: false, - inverted: false, - }, + pos: position{line: 29, col: 8, offset: 572}, + expr: &ruleRefExpr{ + pos: position{line: 29, col: 8, offset: 572}, + name: "Char", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 32, col: 16, offset: 623}, + pos: position{line: 29, col: 16, offset: 580}, expr: &choiceExpr{ - pos: position{line: 32, col: 17, offset: 624}, + pos: position{line: 29, col: 17, offset: 581}, alternatives: []any{ - &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonGroupNode10, - expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, + &ruleRefExpr{ + pos: position{line: 29, col: 17, offset: 581}, + name: "OperatorColonNode", }, - &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, - run: (*parser).callonGroupNode12, - expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, - val: "=", - ignoreCase: false, - want: "\"=\"", - }, + &ruleRefExpr{ + pos: position{line: 29, col: 37, offset: 601}, + name: "OperatorEqualNode", }, }, }, }, &litMatcher{ - pos: position{line: 32, col: 57, offset: 664}, + pos: position{line: 29, col: 57, offset: 621}, val: "(", ignoreCase: false, want: "\"(\"", }, &labeledExpr{ - pos: position{line: 32, col: 61, offset: 668}, + pos: position{line: 29, col: 61, offset: 625}, label: "v", expr: &ruleRefExpr{ - pos: position{line: 32, col: 63, offset: 670}, + pos: position{line: 29, col: 63, offset: 627}, name: "Nodes", }, }, &litMatcher{ - pos: position{line: 32, col: 69, offset: 676}, + pos: position{line: 29, col: 69, offset: 633}, val: ")", ignoreCase: false, want: "\")\"", @@ -1445,1063 +168,1125 @@ var g = &grammar{ }, }, }, - }, -} - -func (c *current) onAST1(n any) (any, error) { - return buildAST(n, c.text, c.pos) - -} - -func (p *parser) callonAST1() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onAST1(stack["n"]) -} - -func (c *current) onNodes3() (any, error) { - return nil, nil - -} - -func (p *parser) callonNodes3() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNodes3() -} - -func (c *current) onNode7() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode7() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode7() -} - -func (c *current) onNode10() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode10() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode10() -} - -func (c *current) onNode12() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode12() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode12() -} - -func (c *current) onNode3(k, v any) (any, error) { - return buildBooleanNode(k, v, c.text, c.pos) - -} - -func (p *parser) callonNode3() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode3(stack["k"], stack["v"]) -} - -func (c *current) onNode22() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode22() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode22() -} - -func (c *current) onNode26() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode26() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode26() -} - -func (c *current) onNode28() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode28() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode28() -} - -func (c *current) onNode30() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode30() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode30() -} - -func (c *current) onNode32() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode32() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode32() -} - -func (c *current) onNode34() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode34() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode34() -} - -func (c *current) onNode36() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode36() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode36() -} - -func (c *current) onNode48() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode48() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode48() -} - -func (c *current) onNode50() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode50() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode50() -} - -func (c *current) onNode52() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode52() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode52() -} - -func (c *current) onNode54() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode54() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode54() -} - -func (c *current) onNode46() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode46() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode46() -} - -func (c *current) onNode59() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode59() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode59() -} - -func (c *current) onNode61() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode61() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode61() -} - -func (c *current) onNode57() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode57() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode57() -} - -func (c *current) onNode66() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode66() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode66() -} - -func (c *current) onNode68() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode68() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode68() -} - -func (c *current) onNode64() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode64() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode64() -} - -func (c *current) onNode44() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode44() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode44() -} - -func (c *current) onNode75() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode75() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode75() -} - -func (c *current) onNode77() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode77() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode77() -} - -func (c *current) onNode73() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode73() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode73() -} - -func (c *current) onNode82() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode82() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode82() -} - -func (c *current) onNode84() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode84() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode84() -} - -func (c *current) onNode80() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode80() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode80() -} - -func (c *current) onNode89() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode89() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode89() -} - -func (c *current) onNode91() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode91() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode91() -} - -func (c *current) onNode87() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode87() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode87() -} - -func (c *current) onNode97() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode97() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode97() -} - -func (c *current) onNode105() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode105() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode105() -} - -func (c *current) onNode107() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode107() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode107() -} - -func (c *current) onNode103() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode103() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode103() -} - -func (c *current) onNode112() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode112() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode112() -} - -func (c *current) onNode114() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode114() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode114() -} - -func (c *current) onNode110() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode110() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode110() -} - -func (c *current) onNode71() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode71() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode71() -} - -func (c *current) onNode42() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode42() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode42() -} - -func (c *current) onNode120() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode120() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode120() -} - -func (c *current) onNode122() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode122() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode122() -} - -func (c *current) onNode124() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode124() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode124() -} - -func (c *current) onNode126() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode126() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode126() -} - -func (c *current) onNode118() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode118() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode118() -} - -func (c *current) onNode131() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode131() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode131() -} - -func (c *current) onNode133() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode133() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode133() -} - -func (c *current) onNode129() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode129() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode129() -} - -func (c *current) onNode138() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode138() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode138() -} - -func (c *current) onNode140() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode140() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode140() -} - -func (c *current) onNode136() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode136() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode136() -} - -func (c *current) onNode116() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode116() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode116() -} - -func (c *current) onNode146() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode146() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode146() -} - -func (c *current) onNode148() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode148() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode148() -} - -func (c *current) onNode144() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode144() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode144() -} - -func (c *current) onNode153() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode153() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode153() -} - -func (c *current) onNode155() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode155() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode155() -} - -func (c *current) onNode151() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode151() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode151() -} - -func (c *current) onNode160() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode160() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode160() -} - -func (c *current) onNode162() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode162() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode162() -} - -func (c *current) onNode158() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode158() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode158() -} - -func (c *current) onNode168() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode168() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode168() -} - -func (c *current) onNode176() (any, error) { - return c.text, nil - -} - -func (p *parser) callonNode176() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode176() + { + name: "PropertyRestrictionNodes", + pos: position{line: 37, col: 1, offset: 837}, + expr: &choiceExpr{ + pos: position{line: 38, col: 5, offset: 869}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 38, col: 5, offset: 869}, + name: "YesNoPropertyRestrictionNode", + }, + &ruleRefExpr{ + pos: position{line: 39, col: 5, offset: 904}, + name: "DateTimeRestrictionNode", + }, + &ruleRefExpr{ + pos: position{line: 40, col: 5, offset: 934}, + name: "TextPropertyRestrictionNode", + }, + }, + }, + }, + { + name: "YesNoPropertyRestrictionNode", + pos: position{line: 42, col: 1, offset: 963}, + expr: &actionExpr{ + pos: position{line: 43, col: 5, offset: 999}, + run: (*parser).callonYesNoPropertyRestrictionNode1, + expr: &seqExpr{ + pos: position{line: 43, col: 5, offset: 999}, + exprs: []any{ + &labeledExpr{ + pos: position{line: 43, col: 5, offset: 999}, + label: "k", + expr: &oneOrMoreExpr{ + pos: position{line: 43, col: 7, offset: 1001}, + expr: &ruleRefExpr{ + pos: position{line: 43, col: 7, offset: 1001}, + name: "Char", + }, + }, + }, + &choiceExpr{ + pos: position{line: 43, col: 14, offset: 1008}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 43, col: 14, offset: 1008}, + name: "OperatorColonNode", + }, + &ruleRefExpr{ + pos: position{line: 43, col: 34, offset: 1028}, + name: "OperatorEqualNode", + }, + }, + }, + &labeledExpr{ + pos: position{line: 43, col: 53, offset: 1047}, + label: "v", + expr: &choiceExpr{ + pos: position{line: 43, col: 56, offset: 1050}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 43, col: 56, offset: 1050}, + val: "true", + ignoreCase: false, + want: "\"true\"", + }, + &litMatcher{ + pos: position{line: 43, col: 65, offset: 1059}, + val: "false", + ignoreCase: false, + want: "\"false\"", + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "DateTimeRestrictionNode", + pos: position{line: 47, col: 1, offset: 1129}, + expr: &actionExpr{ + pos: position{line: 48, col: 5, offset: 1160}, + run: (*parser).callonDateTimeRestrictionNode1, + expr: &seqExpr{ + pos: position{line: 48, col: 5, offset: 1160}, + exprs: []any{ + &labeledExpr{ + pos: position{line: 48, col: 5, offset: 1160}, + label: "k", + expr: &oneOrMoreExpr{ + pos: position{line: 48, col: 7, offset: 1162}, + expr: &ruleRefExpr{ + pos: position{line: 48, col: 7, offset: 1162}, + name: "Char", + }, + }, + }, + &labeledExpr{ + pos: position{line: 48, col: 13, offset: 1168}, + label: "o", + expr: &choiceExpr{ + pos: position{line: 48, col: 16, offset: 1171}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 48, col: 16, offset: 1171}, + name: "OperatorGreaterOrEqualNode", + }, + &ruleRefExpr{ + pos: position{line: 48, col: 45, offset: 1200}, + name: "OperatorLessOrEqualNode", + }, + &ruleRefExpr{ + pos: position{line: 48, col: 71, offset: 1226}, + name: "OperatorGreaterNode", + }, + &ruleRefExpr{ + pos: position{line: 48, col: 93, offset: 1248}, + name: "OperatorLessNode", + }, + &ruleRefExpr{ + pos: position{line: 48, col: 112, offset: 1267}, + name: "OperatorEqualNode", + }, + &ruleRefExpr{ + pos: position{line: 48, col: 132, offset: 1287}, + name: "OperatorColonNode", + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 48, col: 151, offset: 1306}, + expr: &litMatcher{ + pos: position{line: 48, col: 151, offset: 1306}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + &labeledExpr{ + pos: position{line: 48, col: 156, offset: 1311}, + label: "v", + expr: &seqExpr{ + pos: position{line: 48, col: 159, offset: 1314}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 48, col: 159, offset: 1314}, + name: "FullDate", + }, + &litMatcher{ + pos: position{line: 48, col: 168, offset: 1323}, + val: "T", + ignoreCase: false, + want: "\"T\"", + }, + &ruleRefExpr{ + pos: position{line: 48, col: 172, offset: 1327}, + name: "FullTime", + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 48, col: 182, offset: 1337}, + expr: &litMatcher{ + pos: position{line: 48, col: 182, offset: 1337}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + }, + }, + }, + }, + { + name: "TextPropertyRestrictionNode", + pos: position{line: 52, col: 1, offset: 1408}, + expr: &actionExpr{ + pos: position{line: 53, col: 5, offset: 1443}, + run: (*parser).callonTextPropertyRestrictionNode1, + expr: &seqExpr{ + pos: position{line: 53, col: 5, offset: 1443}, + exprs: []any{ + &labeledExpr{ + pos: position{line: 53, col: 5, offset: 1443}, + label: "k", + expr: &oneOrMoreExpr{ + pos: position{line: 53, col: 7, offset: 1445}, + expr: &ruleRefExpr{ + pos: position{line: 53, col: 7, offset: 1445}, + name: "Char", + }, + }, + }, + &choiceExpr{ + pos: position{line: 53, col: 14, offset: 1452}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 53, col: 14, offset: 1452}, + name: "OperatorColonNode", + }, + &ruleRefExpr{ + pos: position{line: 53, col: 34, offset: 1472}, + name: "OperatorEqualNode", + }, + }, + }, + &labeledExpr{ + pos: position{line: 53, col: 53, offset: 1491}, + label: "v", + expr: &choiceExpr{ + pos: position{line: 53, col: 56, offset: 1494}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 53, col: 56, offset: 1494}, + name: "String", + }, + &oneOrMoreExpr{ + pos: position{line: 53, col: 65, offset: 1503}, + expr: &charClassMatcher{ + pos: position{line: 53, col: 65, offset: 1503}, + val: "[^ ()]", + chars: []rune{' ', '(', ')'}, + ignoreCase: false, + inverted: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "FreeTextKeywordNodes", + pos: position{line: 61, col: 1, offset: 1709}, + expr: &choiceExpr{ + pos: position{line: 62, col: 5, offset: 1737}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 62, col: 5, offset: 1737}, + name: "PhraseNode", + }, + &ruleRefExpr{ + pos: position{line: 63, col: 5, offset: 1754}, + name: "WordNode", + }, + }, + }, + }, + { + name: "PhraseNode", + pos: position{line: 65, col: 1, offset: 1764}, + expr: &actionExpr{ + pos: position{line: 66, col: 6, offset: 1783}, + run: (*parser).callonPhraseNode1, + expr: &seqExpr{ + pos: position{line: 66, col: 6, offset: 1783}, + exprs: []any{ + &zeroOrOneExpr{ + pos: position{line: 66, col: 6, offset: 1783}, + expr: &ruleRefExpr{ + pos: position{line: 66, col: 6, offset: 1783}, + name: "OperatorColonNode", + }, + }, + &ruleRefExpr{ + pos: position{line: 66, col: 25, offset: 1802}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 66, col: 27, offset: 1804}, + label: "v", + expr: &ruleRefExpr{ + pos: position{line: 66, col: 29, offset: 1806}, + name: "String", + }, + }, + &ruleRefExpr{ + pos: position{line: 66, col: 36, offset: 1813}, + name: "_", + }, + &zeroOrOneExpr{ + pos: position{line: 66, col: 38, offset: 1815}, + expr: &ruleRefExpr{ + pos: position{line: 66, col: 38, offset: 1815}, + name: "OperatorColonNode", + }, + }, + }, + }, + }, + }, + { + name: "WordNode", + pos: position{line: 70, col: 1, offset: 1896}, + expr: &actionExpr{ + pos: position{line: 71, col: 6, offset: 1913}, + run: (*parser).callonWordNode1, + expr: &seqExpr{ + pos: position{line: 71, col: 6, offset: 1913}, + exprs: []any{ + &zeroOrOneExpr{ + pos: position{line: 71, col: 6, offset: 1913}, + expr: &ruleRefExpr{ + pos: position{line: 71, col: 6, offset: 1913}, + name: "OperatorColonNode", + }, + }, + &ruleRefExpr{ + pos: position{line: 71, col: 25, offset: 1932}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 71, col: 27, offset: 1934}, + label: "v", + expr: &oneOrMoreExpr{ + pos: position{line: 71, col: 29, offset: 1936}, + expr: &charClassMatcher{ + pos: position{line: 71, col: 29, offset: 1936}, + val: "[^ :()]", + chars: []rune{' ', ':', '(', ')'}, + ignoreCase: false, + inverted: true, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 71, col: 38, offset: 1945}, + name: "_", + }, + &zeroOrOneExpr{ + pos: position{line: 71, col: 40, offset: 1947}, + expr: &ruleRefExpr{ + pos: position{line: 71, col: 40, offset: 1947}, + name: "OperatorColonNode", + }, + }, + }, + }, + }, + }, + { + name: "OperatorBooleanNode", + pos: position{line: 79, col: 1, offset: 2156}, + expr: &actionExpr{ + pos: position{line: 80, col: 5, offset: 2183}, + run: (*parser).callonOperatorBooleanNode1, + expr: &choiceExpr{ + pos: position{line: 80, col: 6, offset: 2184}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 80, col: 6, offset: 2184}, + val: "AND", + ignoreCase: false, + want: "\"AND\"", + }, + &litMatcher{ + pos: position{line: 80, col: 14, offset: 2192}, + val: "OR", + ignoreCase: false, + want: "\"OR\"", + }, + &litMatcher{ + pos: position{line: 80, col: 21, offset: 2199}, + val: "NOT", + ignoreCase: false, + want: "\"NOT\"", + }, + &litMatcher{ + pos: position{line: 80, col: 29, offset: 2207}, + val: "+", + ignoreCase: false, + want: "\"+\"", + }, + &litMatcher{ + pos: position{line: 80, col: 35, offset: 2213}, + val: "-", + ignoreCase: false, + want: "\"-\"", + }, + }, + }, + }, + }, + { + name: "OperatorColonNode", + pos: position{line: 84, col: 1, offset: 2275}, + expr: &actionExpr{ + pos: position{line: 85, col: 5, offset: 2300}, + run: (*parser).callonOperatorColonNode1, + expr: &litMatcher{ + pos: position{line: 85, col: 5, offset: 2300}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + }, + }, + { + name: "OperatorEqualNode", + pos: position{line: 89, col: 1, offset: 2361}, + expr: &actionExpr{ + pos: position{line: 90, col: 5, offset: 2386}, + run: (*parser).callonOperatorEqualNode1, + expr: &litMatcher{ + pos: position{line: 90, col: 5, offset: 2386}, + val: "=", + ignoreCase: false, + want: "\"=\"", + }, + }, + }, + { + name: "OperatorLessNode", + pos: position{line: 94, col: 1, offset: 2447}, + expr: &actionExpr{ + pos: position{line: 95, col: 5, offset: 2471}, + run: (*parser).callonOperatorLessNode1, + expr: &litMatcher{ + pos: position{line: 95, col: 5, offset: 2471}, + val: "<", + ignoreCase: false, + want: "\"<\"", + }, + }, + }, + { + name: "OperatorLessOrEqualNode", + pos: position{line: 99, col: 1, offset: 2532}, + expr: &actionExpr{ + pos: position{line: 100, col: 5, offset: 2563}, + run: (*parser).callonOperatorLessOrEqualNode1, + expr: &litMatcher{ + pos: position{line: 100, col: 5, offset: 2563}, + val: "<=", + ignoreCase: false, + want: "\"<=\"", + }, + }, + }, + { + name: "OperatorGreaterNode", + pos: position{line: 104, col: 1, offset: 2625}, + expr: &actionExpr{ + pos: position{line: 105, col: 5, offset: 2652}, + run: (*parser).callonOperatorGreaterNode1, + expr: &litMatcher{ + pos: position{line: 105, col: 5, offset: 2652}, + val: ">", + ignoreCase: false, + want: "\">\"", + }, + }, + }, + { + name: "OperatorGreaterOrEqualNode", + pos: position{line: 109, col: 1, offset: 2713}, + expr: &actionExpr{ + pos: position{line: 110, col: 5, offset: 2747}, + run: (*parser).callonOperatorGreaterOrEqualNode1, + expr: &litMatcher{ + pos: position{line: 110, col: 5, offset: 2747}, + val: ">=", + ignoreCase: false, + want: "\">=\"", + }, + }, + }, + { + name: "TimeYear", + pos: position{line: 119, col: 1, offset: 2933}, + expr: &actionExpr{ + pos: position{line: 120, col: 5, offset: 2949}, + run: (*parser).callonTimeYear1, + expr: &seqExpr{ + pos: position{line: 120, col: 5, offset: 2949}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 120, col: 5, offset: 2949}, + name: "Digit", + }, + &ruleRefExpr{ + pos: position{line: 120, col: 11, offset: 2955}, + name: "Digit", + }, + &ruleRefExpr{ + pos: position{line: 120, col: 17, offset: 2961}, + name: "Digit", + }, + &ruleRefExpr{ + pos: position{line: 120, col: 23, offset: 2967}, + name: "Digit", + }, + }, + }, + }, + }, + { + name: "TimeMonth", + pos: position{line: 124, col: 1, offset: 3009}, + expr: &actionExpr{ + pos: position{line: 125, col: 5, offset: 3026}, + run: (*parser).callonTimeMonth1, + expr: &seqExpr{ + pos: position{line: 125, col: 5, offset: 3026}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 125, col: 5, offset: 3026}, + name: "Digit", + }, + &ruleRefExpr{ + pos: position{line: 125, col: 11, offset: 3032}, + name: "Digit", + }, + }, + }, + }, + }, + { + name: "TimeDay", + pos: position{line: 129, col: 1, offset: 3074}, + expr: &actionExpr{ + pos: position{line: 130, col: 5, offset: 3089}, + run: (*parser).callonTimeDay1, + expr: &seqExpr{ + pos: position{line: 130, col: 5, offset: 3089}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 130, col: 5, offset: 3089}, + name: "Digit", + }, + &ruleRefExpr{ + pos: position{line: 130, col: 11, offset: 3095}, + name: "Digit", + }, + }, + }, + }, + }, + { + name: "TimeHour", + pos: position{line: 134, col: 1, offset: 3137}, + expr: &actionExpr{ + pos: position{line: 135, col: 5, offset: 3153}, + run: (*parser).callonTimeHour1, + expr: &seqExpr{ + pos: position{line: 135, col: 5, offset: 3153}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 135, col: 5, offset: 3153}, + name: "Digit", + }, + &ruleRefExpr{ + pos: position{line: 135, col: 11, offset: 3159}, + name: "Digit", + }, + }, + }, + }, + }, + { + name: "TimeMinute", + pos: position{line: 139, col: 1, offset: 3201}, + expr: &actionExpr{ + pos: position{line: 140, col: 5, offset: 3219}, + run: (*parser).callonTimeMinute1, + expr: &seqExpr{ + pos: position{line: 140, col: 5, offset: 3219}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 140, col: 5, offset: 3219}, + name: "Digit", + }, + &ruleRefExpr{ + pos: position{line: 140, col: 11, offset: 3225}, + name: "Digit", + }, + }, + }, + }, + }, + { + name: "TimeSecond", + pos: position{line: 144, col: 1, offset: 3267}, + expr: &actionExpr{ + pos: position{line: 145, col: 5, offset: 3285}, + run: (*parser).callonTimeSecond1, + expr: &seqExpr{ + pos: position{line: 145, col: 5, offset: 3285}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 145, col: 5, offset: 3285}, + name: "Digit", + }, + &ruleRefExpr{ + pos: position{line: 145, col: 11, offset: 3291}, + name: "Digit", + }, + }, + }, + }, + }, + { + name: "FullDate", + pos: position{line: 149, col: 1, offset: 3333}, + expr: &actionExpr{ + pos: position{line: 150, col: 5, offset: 3349}, + run: (*parser).callonFullDate1, + expr: &seqExpr{ + pos: position{line: 150, col: 5, offset: 3349}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 150, col: 5, offset: 3349}, + name: "TimeYear", + }, + &litMatcher{ + pos: position{line: 150, col: 14, offset: 3358}, + val: "-", + ignoreCase: false, + want: "\"-\"", + }, + &ruleRefExpr{ + pos: position{line: 150, col: 18, offset: 3362}, + name: "TimeMonth", + }, + &litMatcher{ + pos: position{line: 150, col: 28, offset: 3372}, + val: "-", + ignoreCase: false, + want: "\"-\"", + }, + &ruleRefExpr{ + pos: position{line: 150, col: 32, offset: 3376}, + name: "TimeDay", + }, + }, + }, + }, + }, + { + name: "FullTime", + pos: position{line: 154, col: 1, offset: 3420}, + expr: &actionExpr{ + pos: position{line: 155, col: 5, offset: 3436}, + run: (*parser).callonFullTime1, + expr: &seqExpr{ + pos: position{line: 155, col: 5, offset: 3436}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 155, col: 5, offset: 3436}, + name: "TimeHour", + }, + &litMatcher{ + pos: position{line: 155, col: 14, offset: 3445}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + &ruleRefExpr{ + pos: position{line: 155, col: 18, offset: 3449}, + name: "TimeMinute", + }, + &litMatcher{ + pos: position{line: 155, col: 29, offset: 3460}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + &ruleRefExpr{ + pos: position{line: 155, col: 33, offset: 3464}, + name: "TimeSecond", + }, + &zeroOrOneExpr{ + pos: position{line: 155, col: 44, offset: 3475}, + expr: &seqExpr{ + pos: position{line: 155, col: 45, offset: 3476}, + exprs: []any{ + &litMatcher{ + pos: position{line: 155, col: 45, offset: 3476}, + val: ".", + ignoreCase: false, + want: "\".\"", + }, + &oneOrMoreExpr{ + pos: position{line: 155, col: 49, offset: 3480}, + expr: &ruleRefExpr{ + pos: position{line: 155, col: 49, offset: 3480}, + name: "Digit", + }, + }, + }, + }, + }, + &choiceExpr{ + pos: position{line: 155, col: 59, offset: 3490}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 155, col: 59, offset: 3490}, + val: "Z", + ignoreCase: false, + want: "\"Z\"", + }, + &seqExpr{ + pos: position{line: 155, col: 65, offset: 3496}, + exprs: []any{ + &choiceExpr{ + pos: position{line: 155, col: 66, offset: 3497}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 155, col: 66, offset: 3497}, + val: "+", + ignoreCase: false, + want: "\"+\"", + }, + &litMatcher{ + pos: position{line: 155, col: 72, offset: 3503}, + val: "-", + ignoreCase: false, + want: "\"-\"", + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 155, col: 77, offset: 3508}, + name: "TimeHour", + }, + &litMatcher{ + pos: position{line: 155, col: 86, offset: 3517}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + &ruleRefExpr{ + pos: position{line: 155, col: 90, offset: 3521}, + name: "TimeMinute", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Char", + pos: position{line: 163, col: 1, offset: 3692}, + expr: &actionExpr{ + pos: position{line: 164, col: 5, offset: 3704}, + run: (*parser).callonChar1, + expr: &charClassMatcher{ + pos: position{line: 164, col: 5, offset: 3704}, + val: "[A-Za-z]", + ranges: []rune{'A', 'Z', 'a', 'z'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + { + name: "String", + pos: position{line: 168, col: 1, offset: 3749}, + expr: &actionExpr{ + pos: position{line: 169, col: 5, offset: 3763}, + run: (*parser).callonString1, + expr: &seqExpr{ + pos: position{line: 169, col: 5, offset: 3763}, + exprs: []any{ + &litMatcher{ + pos: position{line: 169, col: 5, offset: 3763}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + &labeledExpr{ + pos: position{line: 169, col: 9, offset: 3767}, + label: "v", + expr: &zeroOrMoreExpr{ + pos: position{line: 169, col: 11, offset: 3769}, + expr: &charClassMatcher{ + pos: position{line: 169, col: 11, offset: 3769}, + val: "[^\"]", + chars: []rune{'"'}, + ignoreCase: false, + inverted: true, + }, + }, + }, + &litMatcher{ + pos: position{line: 169, col: 17, offset: 3775}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + }, + }, + }, + { + name: "Digit", + pos: position{line: 173, col: 1, offset: 3810}, + expr: &actionExpr{ + pos: position{line: 174, col: 5, offset: 3823}, + run: (*parser).callonDigit1, + expr: &charClassMatcher{ + pos: position{line: 174, col: 5, offset: 3823}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + { + name: "_", + pos: position{line: 178, col: 1, offset: 3865}, + expr: &zeroOrMoreExpr{ + pos: position{line: 179, col: 5, offset: 3874}, + expr: &charClassMatcher{ + pos: position{line: 179, col: 5, offset: 3874}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, } -func (c *current) onNode178() (any, error) { - return c.text, nil +func (c *current) onAST1(nodes any) (any, error) { + return buildAST(nodes, c.text, c.pos) } -func (p *parser) callonNode178() (any, error) { +func (p *parser) callonAST1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode178() + return p.cur.onAST1(stack["nodes"]) } -func (c *current) onNode174() (any, error) { - return c.text, nil +func (c *current) onNodes1(head, tail any) (any, error) { + return buildNodes(head, tail) } -func (p *parser) callonNode174() (any, error) { +func (p *parser) callonNodes1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode174() + return p.cur.onNodes1(stack["head"], stack["tail"]) } -func (c *current) onNode183() (any, error) { - return c.text, nil +func (c *current) onGroupNode1(k, v any) (any, error) { + return buildGroupNode(k, v, c.text, c.pos) } -func (p *parser) callonNode183() (any, error) { +func (p *parser) callonGroupNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode183() + return p.cur.onGroupNode1(stack["k"], stack["v"]) } -func (c *current) onNode185() (any, error) { - return c.text, nil +func (c *current) onYesNoPropertyRestrictionNode1(k, v any) (any, error) { + return buildBooleanNode(k, v, c.text, c.pos) } -func (p *parser) callonNode185() (any, error) { +func (p *parser) callonYesNoPropertyRestrictionNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode185() + return p.cur.onYesNoPropertyRestrictionNode1(stack["k"], stack["v"]) } -func (c *current) onNode181() (any, error) { - return c.text, nil +func (c *current) onDateTimeRestrictionNode1(k, o, v any) (any, error) { + return buildDateTimeNode(k, o, v, c.text, c.pos) } -func (p *parser) callonNode181() (any, error) { +func (p *parser) callonDateTimeRestrictionNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode181() + return p.cur.onDateTimeRestrictionNode1(stack["k"], stack["o"], stack["v"]) } -func (c *current) onNode142() (any, error) { - return c.text, nil +func (c *current) onTextPropertyRestrictionNode1(k, v any) (any, error) { + return buildStringNode(k, v, c.text, c.pos) } -func (p *parser) callonNode142() (any, error) { +func (p *parser) callonTextPropertyRestrictionNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode142() + return p.cur.onTextPropertyRestrictionNode1(stack["k"], stack["v"]) } -func (c *current) onNode18(k, o, v any) (any, error) { - return buildDateTimeNode(k, o, v, c.text, c.pos) +func (c *current) onPhraseNode1(v any) (any, error) { + return buildStringNode("", v, c.text, c.pos) } -func (p *parser) callonNode18() (any, error) { +func (p *parser) callonPhraseNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode18(stack["k"], stack["o"], stack["v"]) + return p.cur.onPhraseNode1(stack["v"]) } -func (c *current) onNode193() (any, error) { - return c.text, nil +func (c *current) onWordNode1(v any) (any, error) { + return buildStringNode("", v, c.text, c.pos) } -func (p *parser) callonNode193() (any, error) { +func (p *parser) callonWordNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode193() + return p.cur.onWordNode1(stack["v"]) } -func (c *current) onNode196() (any, error) { +func (c *current) onOperatorBooleanNode1() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode196() (any, error) { +func (p *parser) callonOperatorBooleanNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode196() + return p.cur.onOperatorBooleanNode1() } -func (c *current) onNode198() (any, error) { +func (c *current) onOperatorColonNode1() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode198() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode198() -} - -func (c *current) onNode202(v any) (any, error) { - return v, nil - -} - -func (p *parser) callonNode202() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode202(stack["v"]) -} - -func (c *current) onNode189(k, v any) (any, error) { - return buildStringNode(k, v, c.text, c.pos) - -} - -func (p *parser) callonNode189() (any, error) { +func (p *parser) callonOperatorColonNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode189(stack["k"], stack["v"]) + return p.cur.onOperatorColonNode1() } -func (c *current) onNode211() (any, error) { +func (c *current) onOperatorEqualNode1() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode211() (any, error) { +func (p *parser) callonOperatorEqualNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode211() + return p.cur.onOperatorEqualNode1() } -func (c *current) onNode215() (any, error) { +func (c *current) onOperatorLessNode1() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode215() (any, error) { +func (p *parser) callonOperatorLessNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode215() + return p.cur.onOperatorLessNode1() } -func (c *current) onNode219() (any, error) { +func (c *current) onOperatorLessOrEqualNode1() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode219() (any, error) { +func (p *parser) callonOperatorLessOrEqualNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode219() + return p.cur.onOperatorLessOrEqualNode1() } -func (c *current) onNode224() (any, error) { +func (c *current) onOperatorGreaterNode1() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode224() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode224() -} - -func (c *current) onNode226() (any, error) { - return nil, nil - -} - -func (p *parser) callonNode226() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode226() -} - -func (c *current) onNode230(v any) (any, error) { - return v, nil - -} - -func (p *parser) callonNode230() (any, error) { +func (p *parser) callonOperatorGreaterNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode230(stack["v"]) + return p.cur.onOperatorGreaterNode1() } -func (c *current) onNode237(v any) (any, error) { - return nil, nil +func (c *current) onOperatorGreaterOrEqualNode1() (any, error) { + return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode237() (any, error) { +func (p *parser) callonOperatorGreaterOrEqualNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode237(stack["v"]) + return p.cur.onOperatorGreaterOrEqualNode1() } -func (c *current) onNode241() (any, error) { - return buildOperatorNode(c.text, c.pos) +func (c *current) onTimeYear1() (any, error) { + return c.text, nil } -func (p *parser) callonNode241() (any, error) { +func (p *parser) callonTimeYear1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode241() + return p.cur.onTimeYear1() } -func (c *current) onNode221(v any) (any, error) { - return buildStringNode("", v, c.text, c.pos) +func (c *current) onTimeMonth1() (any, error) { + return c.text, nil } -func (p *parser) callonNode221() (any, error) { +func (p *parser) callonTimeMonth1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode221(stack["v"]) + return p.cur.onTimeMonth1() } -func (c *current) onNode246() (any, error) { - return buildOperatorNode(c.text, c.pos) +func (c *current) onTimeDay1() (any, error) { + return c.text, nil } -func (p *parser) callonNode246() (any, error) { +func (p *parser) callonTimeDay1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode246() + return p.cur.onTimeDay1() } -func (c *current) onNode248() (any, error) { - return nil, nil +func (c *current) onTimeHour1() (any, error) { + return c.text, nil } -func (p *parser) callonNode248() (any, error) { +func (p *parser) callonTimeHour1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode248() + return p.cur.onTimeHour1() } -func (c *current) onNode254(v any) (any, error) { - return nil, nil +func (c *current) onTimeMinute1() (any, error) { + return c.text, nil } -func (p *parser) callonNode254() (any, error) { +func (p *parser) callonTimeMinute1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode254(stack["v"]) + return p.cur.onTimeMinute1() } -func (c *current) onNode258() (any, error) { - return buildOperatorNode(c.text, c.pos) +func (c *current) onTimeSecond1() (any, error) { + return c.text, nil } -func (p *parser) callonNode258() (any, error) { +func (p *parser) callonTimeSecond1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode258() + return p.cur.onTimeSecond1() } -func (c *current) onNode243(v any) (any, error) { - return buildStringNode("", v, c.text, c.pos) +func (c *current) onFullDate1() (any, error) { + return c.text, nil } -func (p *parser) callonNode243() (any, error) { +func (p *parser) callonFullDate1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode243(stack["v"]) + return p.cur.onFullDate1() } -func (c *current) onGroupNode6() (any, error) { +func (c *current) onFullTime1() (any, error) { return c.text, nil } -func (p *parser) callonGroupNode6() (any, error) { +func (p *parser) callonFullTime1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onGroupNode6() + return p.cur.onFullTime1() } -func (c *current) onGroupNode10() (any, error) { - return buildOperatorNode(c.text, c.pos) +func (c *current) onChar1() (any, error) { + return c.text, nil } -func (p *parser) callonGroupNode10() (any, error) { +func (p *parser) callonChar1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onGroupNode10() + return p.cur.onChar1() } -func (c *current) onGroupNode12() (any, error) { - return buildOperatorNode(c.text, c.pos) +func (c *current) onString1(v any) (any, error) { + return v, nil } -func (p *parser) callonGroupNode12() (any, error) { +func (p *parser) callonString1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onGroupNode12() + return p.cur.onString1(stack["v"]) } -func (c *current) onGroupNode1(k, v any) (any, error) { - return buildGroupNode(k, v, c.text, c.pos) +func (c *current) onDigit1() (any, error) { + return c.text, nil } -func (p *parser) callonGroupNode1() (any, error) { +func (p *parser) callonDigit1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onGroupNode1(stack["k"], stack["v"]) + return p.cur.onDigit1() } var ( @@ -2556,6 +1341,62 @@ func Entrypoint(ruleName string) Option { } } +// Statistics adds a user provided Stats struct to the parser to allow +// the user to process the results after the parsing has finished. +// Also the key for the "no match" counter is set. +// +// Example usage: +// +// input := "input" +// stats := Stats{} +// _, err := Parse("input-file", []byte(input), Statistics(&stats, "no match")) +// if err != nil { +// log.Panicln(err) +// } +// b, err := json.MarshalIndent(stats.ChoiceAltCnt, "", " ") +// if err != nil { +// log.Panicln(err) +// } +// fmt.Println(string(b)) +func Statistics(stats *Stats, choiceNoMatch string) Option { + return func(p *parser) Option { + oldStats := p.Stats + p.Stats = stats + oldChoiceNoMatch := p.choiceNoMatch + p.choiceNoMatch = choiceNoMatch + if p.Stats.ChoiceAltCnt == nil { + p.Stats.ChoiceAltCnt = make(map[string]map[string]int) + } + return Statistics(oldStats, oldChoiceNoMatch) + } +} + +// Debug creates an Option to set the debug flag to b. When set to true, +// debugging information is printed to stdout while parsing. +// +// The default is false. +func Debug(b bool) Option { + return func(p *parser) Option { + old := p.debug + p.debug = b + return Debug(old) + } +} + +// Memoize creates an Option to set the memoize flag to b. When set to true, +// the parser will cache all results so each expression is evaluated only +// once. This guarantees linear parsing time even for pathological cases, +// at the expense of more memory and slower times for typical cases. +// +// The default is false. +func Memoize(b bool) Option { + return func(p *parser) Option { + old := p.memoize + p.memoize = b + return Memoize(old) + } +} + // AllowInvalidUTF8 creates an Option to allow invalid UTF-8 bytes. // Every invalid UTF-8 byte is treated as a utf8.RuneError (U+FFFD) // by character class matchers and is matched by the any matcher. @@ -2594,6 +1435,16 @@ func GlobalStore(key string, value any) Option { } } +// InitState creates an Option to set a key to a certain value in +// the global "state" store. +func InitState(key string, value any) Option { + return func(p *parser) Option { + old := p.cur.state[key] + p.cur.state[key] = value + return InitState(key, old) + } +} + // ParseFile parses the file identified by filename. func ParseFile(filename string, opts ...Option) (i any, err error) { f, err := os.Open(filename) @@ -2646,6 +1497,11 @@ type current struct { pos position // start position of the match text []byte // raw text of the match + // state is a store for arbitrary key,value pairs that the user wants to be + // tied to the backtracking of the parser. + // This is always rolled back if a parsing rule fails. + state storeDict + // globalStore is a general store for the user to store arbitrary key-value // pairs that they need to manage and that they do not want tied to the // backtracking of the parser. This is only modified by the user and never @@ -2722,6 +1578,11 @@ type ruleRefExpr struct { name string } +type stateCodeExpr struct { + pos position + run func(*parser) error +} + type andCodeExpr struct { pos position run func(*parser) (bool, error) @@ -2825,6 +1686,7 @@ func newParser(filename string, b []byte, opts ...Option) *parser { pt: savepoint{position: position{line: 1}}, recover: true, cur: current{ + state: make(storeDict), globalStore: make(storeDict), }, maxFailPos: position{col: 1, line: 1}, @@ -2889,6 +1751,12 @@ type parser struct { depth int recover bool + debug bool + + memoize bool + // memoization table for the packrat algorithm: + // map[offset in source] map[expression or rule] {value, match} + memo map[int]map[any]resultTuple // rules table, maps the rule identifier to the rule node rules map[string]*rule @@ -2973,6 +1841,26 @@ func (p *parser) popRecovery() { p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)-1] } +func (p *parser) print(prefix, s string) string { + if !p.debug { + return s + } + + fmt.Printf("%s %d:%d:%d: %s [%#U]\n", + prefix, p.pt.line, p.pt.col, p.pt.offset, s, p.pt.rn) + return s +} + +func (p *parser) in(s string) string { + p.depth++ + return p.print(strings.Repeat(" ", p.depth)+">", s) +} + +func (p *parser) out(s string) string { + p.depth-- + return p.print(strings.Repeat(" ", p.depth)+"<", s) +} + func (p *parser) addErr(err error) { p.addErrAt(err, p.pt.position, []string{}) } @@ -3041,17 +1929,93 @@ func (p *parser) read() { // restore parser position to the savepoint pt. func (p *parser) restore(pt savepoint) { + if p.debug { + defer p.out(p.in("restore")) + } if pt.offset == p.pt.offset { return } p.pt = pt } +// Cloner is implemented by any value that has a Clone method, which returns a +// copy of the value. This is mainly used for types which are not passed by +// value (e.g map, slice, chan) or structs that contain such types. +// +// This is used in conjunction with the global state feature to create proper +// copies of the state to allow the parser to properly restore the state in +// the case of backtracking. +type Cloner interface { + Clone() any +} + +var statePool = &sync.Pool{ + New: func() any { return make(storeDict) }, +} + +func (sd storeDict) Discard() { + for k := range sd { + delete(sd, k) + } + statePool.Put(sd) +} + +// clone and return parser current state. +func (p *parser) cloneState() storeDict { + if p.debug { + defer p.out(p.in("cloneState")) + } + + state := statePool.Get().(storeDict) + for k, v := range p.cur.state { + if c, ok := v.(Cloner); ok { + state[k] = c.Clone() + } else { + state[k] = v + } + } + return state +} + +// restore parser current state to the state storeDict. +// every restoreState should applied only one time for every cloned state +func (p *parser) restoreState(state storeDict) { + if p.debug { + defer p.out(p.in("restoreState")) + } + p.cur.state.Discard() + p.cur.state = state +} + // 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) getMemoized(node any) (resultTuple, bool) { + if len(p.memo) == 0 { + return resultTuple{}, false + } + m := p.memo[p.pt.offset] + if len(m) == 0 { + return resultTuple{}, false + } + res, ok := m[node] + return res, ok +} + +func (p *parser) setMemoized(pt savepoint, node any, tuple resultTuple) { + if p.memo == nil { + p.memo = make(map[int]map[any]resultTuple) + } + m := p.memo[pt.offset] + if m == nil { + m = make(map[any]resultTuple) + p.memo[pt.offset] = m + } + m[node] = tuple +} + func (p *parser) buildRulesTable(g *grammar) { p.rules = make(map[string]*rule, len(g.rules)) for _, r := range g.rules { @@ -3073,6 +2037,9 @@ func (p *parser) parse(g *grammar) (val any, err error) { // and return the panic as an error. defer func() { if e := recover(); e != nil { + if p.debug { + defer p.out(p.in("panic handler")) + } val = nil switch e := e.(type) { case error: @@ -3134,15 +2101,45 @@ func listJoin(list []string, sep string, lastSep string) string { } func (p *parser) parseRule(rule *rule) (any, bool) { + if p.debug { + defer p.out(p.in("parseRule " + rule.name)) + } + + if p.memoize { + res, ok := p.getMemoized(rule) + if ok { + p.restore(res.end) + return res.v, res.b + } + } + + start := p.pt p.rstack = append(p.rstack, rule) p.pushV() val, ok := p.parseExpr(rule.expr) p.popV() p.rstack = p.rstack[:len(p.rstack)-1] + if ok && p.debug { + p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) + } + + if p.memoize { + p.setMemoized(start, rule, resultTuple{val, ok, p.pt}) + } return val, ok } func (p *parser) parseExpr(expr any) (any, bool) { + var pt savepoint + + if p.memoize { + res, ok := p.getMemoized(expr) + if ok { + p.restore(res.end) + return res.v, res.b + } + pt = p.pt + } p.ExprCnt++ if p.ExprCnt > p.maxExprCnt { @@ -3180,6 +2177,8 @@ func (p *parser) parseExpr(expr any) (any, bool) { val, ok = p.parseRuleRefExpr(expr) case *seqExpr: val, ok = p.parseSeqExpr(expr) + case *stateCodeExpr: + val, ok = p.parseStateCodeExpr(expr) case *throwExpr: val, ok = p.parseThrowExpr(expr) case *zeroOrMoreExpr: @@ -3189,46 +2188,74 @@ func (p *parser) parseExpr(expr any) (any, bool) { default: panic(fmt.Sprintf("unknown expression type %T", expr)) } + if p.memoize { + p.setMemoized(pt, expr, resultTuple{val, ok, p.pt}) + } return val, ok } func (p *parser) parseActionExpr(act *actionExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseActionExpr")) + } + start := p.pt val, ok := p.parseExpr(act.expr) if ok { p.cur.pos = start.position p.cur.text = p.sliceFrom(start) + state := p.cloneState() actVal, err := act.run(p) if err != nil { p.addErrAt(err, start.position, []string{}) } + p.restoreState(state) val = actVal } + if ok && p.debug { + p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) + } return val, ok } func (p *parser) parseAndCodeExpr(and *andCodeExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseAndCodeExpr")) + } + + state := p.cloneState() ok, err := and.run(p) if err != nil { p.addErr(err) } + p.restoreState(state) return nil, ok } func (p *parser) parseAndExpr(and *andExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseAndExpr")) + } + pt := p.pt + state := p.cloneState() p.pushV() _, ok := p.parseExpr(and.expr) p.popV() + p.restoreState(state) p.restore(pt) return nil, ok } func (p *parser) parseAnyMatcher(any *anyMatcher) (any, bool) { + if p.debug { + defer p.out(p.in("parseAnyMatcher")) + } + if p.pt.rn == utf8.RuneError && p.pt.w == 0 { // EOF - see utf8.DecodeRune p.failAt(false, p.pt.position, ".") @@ -3241,6 +2268,10 @@ func (p *parser) parseAnyMatcher(any *anyMatcher) (any, bool) { } func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (any, bool) { + if p.debug { + defer p.out(p.in("parseCharClassMatcher")) + } + cur := p.pt.rn start := p.pt @@ -3302,22 +2333,50 @@ func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (any, bool) { return nil, false } +func (p *parser) incChoiceAltCnt(ch *choiceExpr, altI int) { + choiceIdent := fmt.Sprintf("%s %d:%d", p.rstack[len(p.rstack)-1].name, ch.pos.line, ch.pos.col) + m := p.ChoiceAltCnt[choiceIdent] + if m == nil { + m = make(map[string]int) + p.ChoiceAltCnt[choiceIdent] = m + } + // We increment altI by 1, so the keys do not start at 0 + alt := strconv.Itoa(altI + 1) + if altI == choiceNoMatch { + alt = p.choiceNoMatch + } + m[alt]++ +} + func (p *parser) parseChoiceExpr(ch *choiceExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseChoiceExpr")) + } + for altI, alt := range ch.alternatives { // dummy assignment to prevent compile error if optimized _ = altI + state := p.cloneState() + p.pushV() val, ok := p.parseExpr(alt) p.popV() if ok { + p.incChoiceAltCnt(ch, altI) return val, ok } + p.restoreState(state) } + p.incChoiceAltCnt(ch, choiceNoMatch) return nil, false } func (p *parser) parseLabeledExpr(lab *labeledExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseLabeledExpr")) + } + p.pushV() val, ok := p.parseExpr(lab.expr) p.popV() @@ -3329,6 +2388,10 @@ func (p *parser) parseLabeledExpr(lab *labeledExpr) (any, bool) { } func (p *parser) parseLitMatcher(lit *litMatcher) (any, bool) { + if p.debug { + defer p.out(p.in("parseLitMatcher")) + } + start := p.pt for _, want := range lit.val { cur := p.pt.rn @@ -3347,27 +2410,44 @@ func (p *parser) parseLitMatcher(lit *litMatcher) (any, bool) { } func (p *parser) parseNotCodeExpr(not *notCodeExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseNotCodeExpr")) + } + + state := p.cloneState() + ok, err := not.run(p) if err != nil { p.addErr(err) } + p.restoreState(state) return nil, !ok } func (p *parser) parseNotExpr(not *notExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseNotExpr")) + } + pt := p.pt + state := p.cloneState() p.pushV() p.maxFailInvertExpected = !p.maxFailInvertExpected _, ok := p.parseExpr(not.expr) p.maxFailInvertExpected = !p.maxFailInvertExpected p.popV() + p.restoreState(state) p.restore(pt) return nil, !ok } func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseOneOrMoreExpr")) + } + var vals []any for { @@ -3386,6 +2466,9 @@ func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (any, bool) { } func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseRecoveryExpr (" + strings.Join(recover.failureLabel, ",") + ")")) + } p.pushRecovery(recover.failureLabel, recover.recoverExpr) val, ok := p.parseExpr(recover.expr) @@ -3395,6 +2478,10 @@ func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (any, bool) { } func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseRuleRefExpr " + ref.name)) + } + if ref.name == "" { panic(fmt.Sprintf("%s: invalid rule: missing name", ref.pos)) } @@ -3408,12 +2495,18 @@ func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (any, bool) { } func (p *parser) parseSeqExpr(seq *seqExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseSeqExpr")) + } + vals := make([]any, 0, len(seq.exprs)) pt := p.pt + state := p.cloneState() for _, expr := range seq.exprs { val, ok := p.parseExpr(expr) if !ok { + p.restoreState(state) p.restore(pt) return nil, false } @@ -3422,7 +2515,22 @@ func (p *parser) parseSeqExpr(seq *seqExpr) (any, bool) { return vals, true } +func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseStateCodeExpr")) + } + + err := state.run(p) + if err != nil { + p.addErr(err) + } + return nil, true +} + func (p *parser) parseThrowExpr(expr *throwExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseThrowExpr")) + } for i := len(p.recoveryStack) - 1; i >= 0; i-- { if recoverExpr, ok := p.recoveryStack[i][expr.label]; ok { @@ -3436,6 +2544,10 @@ func (p *parser) parseThrowExpr(expr *throwExpr) (any, bool) { } func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseZeroOrMoreExpr")) + } + var vals []any for { @@ -3450,6 +2562,10 @@ func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (any, bool) { } func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (any, bool) { + if p.debug { + defer p.out(p.in("parseZeroOrOneExpr")) + } + p.pushV() val, _ := p.parseExpr(expr.expr) p.popV() diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index 7e86456a999..e60645f4ab1 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -5,7 +5,6 @@ import ( "testing" "time" - "github.com/araddon/dateparse" tAssert "github.com/stretchr/testify/assert" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" @@ -14,7 +13,7 @@ import ( ) var mustParseTime = func(t *testing.T, ts string) time.Time { - tp, err := dateparse.ParseLocal(ts) + tp, err := time.Parse(time.RFC3339Nano, ts) if err != nil { t.Fatalf("time.Parse(...) error = %v", err) } @@ -62,92 +61,15 @@ func TestParse(t *testing.T) { expectedError error }{ // SPEC ////////////////////////////////////////////////////////////////////////////// - // // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf - // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 - // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference // - // ++ - // 2.1.2 AND Operator - // 3.1.2 AND Operator - { - name: `cat AND dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - name: `AND`, - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: `AND cat AND dog`, - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - // ++ - // 2.1.6 NOT Operator - // 3.1.6 NOT Operator - { - name: `cat NOT dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - name: `NOT dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - // ++ - // 2.1.8 OR Operator - // 3.1.8 OR Operator - { - name: `cat OR dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - name: `OR`, - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: `OR cat AND dog`, - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - // ++ // 3.1.11 Implicit Operator { name: `cat dog`, expectedAst: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "dog"}, }, }, @@ -180,8 +102,6 @@ func TestParse(t *testing.T) { }, }, }, - // ++ - // 2.1.12 Parentheses // 3.1.12 Parentheses { name: `(cat OR dog) AND fox`, @@ -197,7 +117,6 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 3.2.3 Implicit Operator for Property Restriction { name: `author:"John Smith" filetype:docx`, @@ -259,7 +178,6 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 3.3.1.1.1 Implicit AND Operator { name: `cat +dog`, @@ -334,7 +252,7 @@ func TestParse(t *testing.T) { expectedAst: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "dog"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "fox"}, @@ -364,7 +282,7 @@ func TestParse(t *testing.T) { expectedAst: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "dog"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.OperatorNode{Value: kql.BoolNOT}, @@ -423,37 +341,6 @@ func TestParse(t *testing.T) { }, }, }, - // ++ - // 2.3.5 Date Tokens - // 3.3.5 Date Tokens - { - name: `Modified:2023-09-05`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05"), - }, - }, - }, - }, - { - name: `Modified:"2008-01-29"`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2008-01-29"), - }, - }, - }, - }, - { - name: `Modified:today`, - skip: true, - }, ////////////////////////////////////////////////////////////////////////////////////// // everything else { @@ -462,15 +349,15 @@ func TestParse(t *testing.T) { expectedAst: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "federated"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "federat*"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "fed*"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Key: "author", Value: "John Smith"}, @@ -480,23 +367,23 @@ func TestParse(t *testing.T) { &ast.StringNode{Key: "filename", Value: "budget.xlsx"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Key: "author", Value: "Shakespear"}, @@ -525,7 +412,7 @@ func TestParse(t *testing.T) { Key: "author", Nodes: []ast.Node{ &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "Jane Smith"}, }, }, @@ -590,7 +477,7 @@ func TestParse(t *testing.T) { Key: "author", Nodes: []ast.Node{ &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "Jane"}, }, }, @@ -614,7 +501,7 @@ func TestParse(t *testing.T) { Key: "author", Nodes: []ast.Node{ &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "Jane"}, }, }, @@ -689,7 +576,7 @@ func TestParse(t *testing.T) { &ast.StringNode{ Value: "๐Ÿ˜‚", }, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{ Value: "*๐Ÿ˜€ ๐Ÿ˜*", }, @@ -822,199 +709,6 @@ func TestParse(t *testing.T) { }, }, }, - { - name: "animal:(cat dog turtle)", - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "animal", - Nodes: []ast.Node{ - &ast.StringNode{ - Value: "cat", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "dog", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "turtle", - }, - }, - }, - }, - }, - }, - { - name: "(cat dog turtle)", - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Nodes: []ast.Node{ - &ast.StringNode{ - Value: "cat", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "dog", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "turtle", - }, - }, - }, - }, - }, - }, - { - name: "animal:(mammal:cat mammal:dog reptile:turtle)", - expectedError: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "cat"}, - }, - }, - { - name: "animal:(cat mammal:dog turtle)", - expectedError: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "dog"}, - }, - }, - { - name: "animal:(AND cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: "animal:(OR cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: "(AND cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: "(OR cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: `cat dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - name: `cat dog fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, - }, - }, - }, - { - name: `(cat dog) fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, - }, - }, - }, - { - name: `(mammal:cat mammal:dog) fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "mammal", Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "mammal", Value: "dog"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, - }, - }, - }, - { - name: `mammal:(cat dog) fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "mammal", - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, - }, - }, - }, - { - name: `mammal:(cat dog) mammal:fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "mammal", - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "mammal", Value: "fox"}, - }, - }, - }, - { - name: `title:((Advanced OR Search OR Query) -"Advanced Search Query")`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "title", - Nodes: []ast.Node{ - &ast.GroupNode{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "Advanced"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Search"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Query"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "Advanced Search Query"}, - }, - }, - }, - }, - }, } assert := tAssert.New(t) @@ -1033,20 +727,17 @@ func TestParse(t *testing.T) { q = tt.givenQuery } - parsedAST, err := kql.Builder{}.Build(q) + parsedAST, err := kql.Parse("", []byte(q)) if tt.expectedError != nil { - if tt.expectedError.Error() != "" { - assert.Equal(err.Error(), tt.expectedError.Error()) - } else { - assert.NotNil(err) - } + assert.Equal(err, tt.expectedError) + assert.Nil(parsedAST) return } if diff := test.DiffAst(tt.expectedAst, parsedAST); diff != "" { - t.Fatalf("AST mismatch \nquery: '%s' \n(-expected +got): %s", q, diff) + t.Fatalf("AST mismatch \nquery: '%s' \n(-want +got): %s", q, diff) } }) } diff --git a/services/search/pkg/query/kql/factory.go b/services/search/pkg/query/kql/factory.go index ff9044886a9..bdee43a8873 100644 --- a/services/search/pkg/query/kql/factory.go +++ b/services/search/pkg/query/kql/factory.go @@ -38,16 +38,35 @@ func buildAST(n interface{}, text []byte, pos position) (*ast.Ast, error) { return nil, err } - a := &ast.Ast{ + return &ast.Ast{ Base: b, - Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolOR}, nodes...), + Nodes: nodes, + }, nil +} + +func buildNodes(head, tail interface{}) ([]ast.Node, error) { + headNode, err := toNode[ast.Node](head) + if err != nil { + return nil, err } - if err := validateAst(a); err != nil { + if tail == nil { + return []ast.Node{headNode}, nil + } + + tailNodes, err := toNodes[ast.Node](tail) + if err != nil { return nil, err } - return a, nil + allNodes := []ast.Node{headNode} + + connectionNode := incorporateNode(headNode, tailNodes...) + if connectionNode != nil { + allNodes = append(allNodes, connectionNode) + } + + return append(allNodes, tailNodes...), nil } func buildStringNode(k, v interface{}, text []byte, pos position) (*ast.StringNode, error) { @@ -162,15 +181,9 @@ func buildGroupNode(k, n interface{}, text []byte, pos position) (*ast.GroupNode return nil, err } - gn := &ast.GroupNode{ + return &ast.GroupNode{ Base: b, Key: key, - Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolOR}, nodes...), - } - - if err := validateGroupNode(gn); err != nil { - return nil, err - } - - return gn, nil + Nodes: nodes, + }, nil } diff --git a/services/search/pkg/query/kql/kql.go b/services/search/pkg/query/kql/kql.go index 8719223b8d2..0566278aa62 100644 --- a/services/search/pkg/query/kql/kql.go +++ b/services/search/pkg/query/kql/kql.go @@ -2,7 +2,7 @@ package kql import ( - "errors" + "strings" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -24,21 +24,117 @@ type Builder struct{} func (b Builder) Build(q string) (*ast.Ast, error) { f, err := Parse("", []byte(q)) if err != nil { - var list errList - errors.As(err, &list) - - for _, listError := range list { - var parserError *parserError - switch { - case errors.As(listError, &parserError): - if parserError.Inner != nil { - return nil, parserError.Inner + return nil, err + } + return f.(*ast.Ast), nil +} + +// incorporateNode connects a leading node with the rest +func incorporateNode(headNode ast.Node, tailNodes ...ast.Node) *ast.OperatorNode { + switch headNode.(type) { + case *ast.OperatorNode: + return nil + } + + var nearestNeighborNode ast.Node + var nearestNeighborOperators []*ast.OperatorNode + +l: + for _, tailNode := range tailNodes { + switch node := tailNode.(type) { + case *ast.OperatorNode: + nearestNeighborOperators = append(nearestNeighborOperators, node) + default: + nearestNeighborNode = node + break l + } + } + + if nearestNeighborNode == nil { + return nil + } + + headKey := strings.ToLower(nodeKey(headNode)) + neighborKey := strings.ToLower(nodeKey(nearestNeighborNode)) + + connection := &ast.OperatorNode{ + Base: &ast.Base{Loc: &ast.Location{Source: &[]string{"implicitly operator"}[0]}}, + Value: BoolAND, + } + + // if the current node and the neighbor node have the same key + // the connection is of type OR, same applies if no keys are in place + // + // "" == "" + // + // spec: same + // author:"John Smith" author:"Jane Smith" + // author:"John Smith" OR author:"Jane Smith" + if headKey == neighborKey { + connection.Value = BoolOR + } + + // decisions based on nearest neighbor node + switch nearestNeighborNode.(type) { + // nearest neighbor node type could change the default case + // docs says, if the next value node: + // + // is a group AND has no key + // + // even if the current node has none too, which normal leads to SAME KEY OR + // + // it should be an AND edge + // + // spec: same + // cat (dog OR fox) + // cat AND (dog OR fox) + // + // note: + // sounds contradictory to me + case *ast.GroupNode: + if headKey == "" && neighborKey == "" { + connection.Value = BoolAND + } + } + + // decisions based on nearest neighbor operators + for i, node := range nearestNeighborOperators { + // consider direct neighbor operator only + if i == 0 { + // no connection is necessary here because an `AND` or `OR` edge is already present + // exit + for _, skipValue := range []string{BoolOR, BoolAND} { + if node.Value == skipValue { + return nil } + } - return nil, listError + // if neighbor node negotiates, AND edge is needed + // + // spec: same + // cat -dog + // cat AND NOT dog + if node.Value == BoolNOT { + connection.Value = BoolAND } } } - return f.(*ast.Ast), nil + return connection +} + +// nodeKey tries to return a node key +func nodeKey(n ast.Node) string { + switch node := n.(type) { + case *ast.StringNode: + return node.Key + case *ast.DateTimeNode: + return node.Key + case *ast.BooleanNode: + return node.Key + case *ast.GroupNode: + return node.Key + default: + return "" + } } diff --git a/services/search/pkg/query/kql/kql_test.go b/services/search/pkg/query/kql/kql_test.go index de050a47413..d18068ed575 100644 --- a/services/search/pkg/query/kql/kql_test.go +++ b/services/search/pkg/query/kql/kql_test.go @@ -5,26 +5,23 @@ import ( tAssert "github.com/stretchr/testify/assert" - "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" "github.com/owncloud/ocis/v2/services/search/pkg/query/kql" ) func TestNewAST(t *testing.T) { tests := []struct { - name string - givenQuery string - expectedError error + name string + givenQuery string + shouldError bool }{ { name: "success", givenQuery: "foo:bar", }, { - name: "error", - givenQuery: kql.BoolAND, - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, + name: "error", + givenQuery: kql.BoolAND, + shouldError: false, }, } @@ -35,20 +32,13 @@ func TestNewAST(t *testing.T) { t.Run(tt.name, func(t *testing.T) { got, err := kql.Builder{}.Build(tt.givenQuery) - if tt.expectedError != nil { - if tt.expectedError.Error() != "" { - assert.Equal(err.Error(), tt.expectedError.Error()) - } else { - assert.NotNil(err) - } - + if tt.shouldError { + assert.NotNil(err) assert.Nil(got) - - return + } else { + assert.Nil(err) + assert.NotNil(got) } - - assert.Nil(err) - assert.NotNil(got) }) } } From 6d223a582d94304b9bfd2e997f27d39ad8369dc6 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Fri, 8 Sep 2023 12:43:00 +0200 Subject: [PATCH 02/11] enhancement: kql parser error if query starts with AND --- services/search/pkg/query/kql/dictionary.peg | 23 +- .../search/pkg/query/kql/dictionary_gen.go | 539 ++++++++++-------- .../search/pkg/query/kql/dictionary_test.go | 29 +- services/search/pkg/query/kql/kql.go | 13 +- services/search/pkg/query/kql/kql_test.go | 2 +- 5 files changed, 365 insertions(+), 241 deletions(-) diff --git a/services/search/pkg/query/kql/dictionary.peg b/services/search/pkg/query/kql/dictionary.peg index 6c177b2d87c..289d3e4bdb8 100644 --- a/services/search/pkg/query/kql/dictionary.peg +++ b/services/search/pkg/query/kql/dictionary.peg @@ -7,7 +7,7 @@ //////////////////////////////////////////////////////// AST <- - _ nodes:Nodes _ { + _ !OperatorBooleanAndNode _ nodes:Nodes _ { return buildAST(nodes, c.text, c.pos) } @@ -15,7 +15,7 @@ Nodes <- _ head:( GroupNode / PropertyRestrictionNodes / - OperatorBooleanNode / + OperatorBooleanNodes / FreeTextKeywordNodes ) _ tail:Nodes? { return buildNodes(head, tail) @@ -76,8 +76,23 @@ WordNode <- // operators //////////////////////////////////////////////////////// -OperatorBooleanNode <- - ("AND" / "OR" / "NOT" / "+" / "-") { +OperatorBooleanNodes <- + OperatorBooleanAndNode / + OperatorBooleanNotNode / + OperatorBooleanOrNode + +OperatorBooleanAndNode <- + ("AND" / "+") { + return buildOperatorNode(c.text, c.pos) + } + +OperatorBooleanNotNode <- + ("NOT" / "-") { + return buildOperatorNode(c.text, c.pos) + } + +OperatorBooleanOrNode <- + ("OR") { return buildOperatorNode(c.text, c.pos) } diff --git a/services/search/pkg/query/kql/dictionary_gen.go b/services/search/pkg/query/kql/dictionary_gen.go index 4c9e749ffbd..67c9959e1c9 100644 --- a/services/search/pkg/query/kql/dictionary_gen.go +++ b/services/search/pkg/query/kql/dictionary_gen.go @@ -32,16 +32,27 @@ var g = &grammar{ pos: position{line: 10, col: 5, offset: 154}, name: "_", }, + ¬Expr{ + pos: position{line: 10, col: 7, offset: 156}, + expr: &ruleRefExpr{ + pos: position{line: 10, col: 8, offset: 157}, + name: "OperatorBooleanAndNode", + }, + }, + &ruleRefExpr{ + pos: position{line: 10, col: 31, offset: 180}, + name: "_", + }, &labeledExpr{ - pos: position{line: 10, col: 7, offset: 156}, + pos: position{line: 10, col: 33, offset: 182}, label: "nodes", expr: &ruleRefExpr{ - pos: position{line: 10, col: 13, offset: 162}, + pos: position{line: 10, col: 39, offset: 188}, name: "Nodes", }, }, &ruleRefExpr{ - pos: position{line: 10, col: 19, offset: 168}, + pos: position{line: 10, col: 45, offset: 194}, name: "_", }, }, @@ -50,53 +61,53 @@ var g = &grammar{ }, { name: "Nodes", - pos: position{line: 14, col: 1, offset: 225}, + pos: position{line: 14, col: 1, offset: 251}, expr: &actionExpr{ - pos: position{line: 15, col: 5, offset: 238}, + pos: position{line: 15, col: 5, offset: 264}, run: (*parser).callonNodes1, expr: &seqExpr{ - pos: position{line: 15, col: 5, offset: 238}, + pos: position{line: 15, col: 5, offset: 264}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 15, col: 5, offset: 238}, + pos: position{line: 15, col: 5, offset: 264}, name: "_", }, &labeledExpr{ - pos: position{line: 15, col: 7, offset: 240}, + pos: position{line: 15, col: 7, offset: 266}, label: "head", expr: &choiceExpr{ - pos: position{line: 16, col: 9, offset: 255}, + pos: position{line: 16, col: 9, offset: 281}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 16, col: 9, offset: 255}, + pos: position{line: 16, col: 9, offset: 281}, name: "GroupNode", }, &ruleRefExpr{ - pos: position{line: 17, col: 9, offset: 275}, + pos: position{line: 17, col: 9, offset: 301}, name: "PropertyRestrictionNodes", }, &ruleRefExpr{ - pos: position{line: 18, col: 9, offset: 310}, - name: "OperatorBooleanNode", + pos: position{line: 18, col: 9, offset: 336}, + name: "OperatorBooleanNodes", }, &ruleRefExpr{ - pos: position{line: 19, col: 9, offset: 340}, + pos: position{line: 19, col: 9, offset: 367}, name: "FreeTextKeywordNodes", }, }, }, }, &ruleRefExpr{ - pos: position{line: 20, col: 7, offset: 367}, + pos: position{line: 20, col: 7, offset: 394}, name: "_", }, &labeledExpr{ - pos: position{line: 20, col: 9, offset: 369}, + pos: position{line: 20, col: 9, offset: 396}, label: "tail", expr: &zeroOrOneExpr{ - pos: position{line: 20, col: 14, offset: 374}, + pos: position{line: 20, col: 14, offset: 401}, expr: &ruleRefExpr{ - pos: position{line: 20, col: 14, offset: 374}, + pos: position{line: 20, col: 14, offset: 401}, name: "Nodes", }, }, @@ -107,59 +118,59 @@ var g = &grammar{ }, { name: "GroupNode", - pos: position{line: 28, col: 1, offset: 552}, + pos: position{line: 28, col: 1, offset: 579}, expr: &actionExpr{ - pos: position{line: 29, col: 5, offset: 569}, + pos: position{line: 29, col: 5, offset: 596}, run: (*parser).callonGroupNode1, expr: &seqExpr{ - pos: position{line: 29, col: 5, offset: 569}, + pos: position{line: 29, col: 5, offset: 596}, exprs: []any{ &labeledExpr{ - pos: position{line: 29, col: 5, offset: 569}, + pos: position{line: 29, col: 5, offset: 596}, label: "k", expr: &zeroOrOneExpr{ - pos: position{line: 29, col: 7, offset: 571}, + pos: position{line: 29, col: 7, offset: 598}, expr: &oneOrMoreExpr{ - pos: position{line: 29, col: 8, offset: 572}, + pos: position{line: 29, col: 8, offset: 599}, expr: &ruleRefExpr{ - pos: position{line: 29, col: 8, offset: 572}, + pos: position{line: 29, col: 8, offset: 599}, name: "Char", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 29, col: 16, offset: 580}, + pos: position{line: 29, col: 16, offset: 607}, expr: &choiceExpr{ - pos: position{line: 29, col: 17, offset: 581}, + pos: position{line: 29, col: 17, offset: 608}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 29, col: 17, offset: 581}, + pos: position{line: 29, col: 17, offset: 608}, name: "OperatorColonNode", }, &ruleRefExpr{ - pos: position{line: 29, col: 37, offset: 601}, + pos: position{line: 29, col: 37, offset: 628}, name: "OperatorEqualNode", }, }, }, }, &litMatcher{ - pos: position{line: 29, col: 57, offset: 621}, + pos: position{line: 29, col: 57, offset: 648}, val: "(", ignoreCase: false, want: "\"(\"", }, &labeledExpr{ - pos: position{line: 29, col: 61, offset: 625}, + pos: position{line: 29, col: 61, offset: 652}, label: "v", expr: &ruleRefExpr{ - pos: position{line: 29, col: 63, offset: 627}, + pos: position{line: 29, col: 63, offset: 654}, name: "Nodes", }, }, &litMatcher{ - pos: position{line: 29, col: 69, offset: 633}, + pos: position{line: 29, col: 69, offset: 660}, val: ")", ignoreCase: false, want: "\")\"", @@ -170,20 +181,20 @@ var g = &grammar{ }, { name: "PropertyRestrictionNodes", - pos: position{line: 37, col: 1, offset: 837}, + pos: position{line: 37, col: 1, offset: 864}, expr: &choiceExpr{ - pos: position{line: 38, col: 5, offset: 869}, + pos: position{line: 38, col: 5, offset: 896}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 38, col: 5, offset: 869}, + pos: position{line: 38, col: 5, offset: 896}, name: "YesNoPropertyRestrictionNode", }, &ruleRefExpr{ - pos: position{line: 39, col: 5, offset: 904}, + pos: position{line: 39, col: 5, offset: 931}, name: "DateTimeRestrictionNode", }, &ruleRefExpr{ - pos: position{line: 40, col: 5, offset: 934}, + pos: position{line: 40, col: 5, offset: 961}, name: "TextPropertyRestrictionNode", }, }, @@ -191,51 +202,51 @@ var g = &grammar{ }, { name: "YesNoPropertyRestrictionNode", - pos: position{line: 42, col: 1, offset: 963}, + pos: position{line: 42, col: 1, offset: 990}, expr: &actionExpr{ - pos: position{line: 43, col: 5, offset: 999}, + pos: position{line: 43, col: 5, offset: 1026}, run: (*parser).callonYesNoPropertyRestrictionNode1, expr: &seqExpr{ - pos: position{line: 43, col: 5, offset: 999}, + pos: position{line: 43, col: 5, offset: 1026}, exprs: []any{ &labeledExpr{ - pos: position{line: 43, col: 5, offset: 999}, + pos: position{line: 43, col: 5, offset: 1026}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 43, col: 7, offset: 1001}, + pos: position{line: 43, col: 7, offset: 1028}, expr: &ruleRefExpr{ - pos: position{line: 43, col: 7, offset: 1001}, + pos: position{line: 43, col: 7, offset: 1028}, name: "Char", }, }, }, &choiceExpr{ - pos: position{line: 43, col: 14, offset: 1008}, + pos: position{line: 43, col: 14, offset: 1035}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 43, col: 14, offset: 1008}, + pos: position{line: 43, col: 14, offset: 1035}, name: "OperatorColonNode", }, &ruleRefExpr{ - pos: position{line: 43, col: 34, offset: 1028}, + pos: position{line: 43, col: 34, offset: 1055}, name: "OperatorEqualNode", }, }, }, &labeledExpr{ - pos: position{line: 43, col: 53, offset: 1047}, + pos: position{line: 43, col: 53, offset: 1074}, label: "v", expr: &choiceExpr{ - pos: position{line: 43, col: 56, offset: 1050}, + pos: position{line: 43, col: 56, offset: 1077}, alternatives: []any{ &litMatcher{ - pos: position{line: 43, col: 56, offset: 1050}, + pos: position{line: 43, col: 56, offset: 1077}, val: "true", ignoreCase: false, want: "\"true\"", }, &litMatcher{ - pos: position{line: 43, col: 65, offset: 1059}, + pos: position{line: 43, col: 65, offset: 1086}, val: "false", ignoreCase: false, want: "\"false\"", @@ -249,93 +260,93 @@ var g = &grammar{ }, { name: "DateTimeRestrictionNode", - pos: position{line: 47, col: 1, offset: 1129}, + pos: position{line: 47, col: 1, offset: 1156}, expr: &actionExpr{ - pos: position{line: 48, col: 5, offset: 1160}, + pos: position{line: 48, col: 5, offset: 1187}, run: (*parser).callonDateTimeRestrictionNode1, expr: &seqExpr{ - pos: position{line: 48, col: 5, offset: 1160}, + pos: position{line: 48, col: 5, offset: 1187}, exprs: []any{ &labeledExpr{ - pos: position{line: 48, col: 5, offset: 1160}, + pos: position{line: 48, col: 5, offset: 1187}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 48, col: 7, offset: 1162}, + pos: position{line: 48, col: 7, offset: 1189}, expr: &ruleRefExpr{ - pos: position{line: 48, col: 7, offset: 1162}, + pos: position{line: 48, col: 7, offset: 1189}, name: "Char", }, }, }, &labeledExpr{ - pos: position{line: 48, col: 13, offset: 1168}, + pos: position{line: 48, col: 13, offset: 1195}, label: "o", expr: &choiceExpr{ - pos: position{line: 48, col: 16, offset: 1171}, + pos: position{line: 48, col: 16, offset: 1198}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 48, col: 16, offset: 1171}, + pos: position{line: 48, col: 16, offset: 1198}, name: "OperatorGreaterOrEqualNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 45, offset: 1200}, + pos: position{line: 48, col: 45, offset: 1227}, name: "OperatorLessOrEqualNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 71, offset: 1226}, + pos: position{line: 48, col: 71, offset: 1253}, name: "OperatorGreaterNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 93, offset: 1248}, + pos: position{line: 48, col: 93, offset: 1275}, name: "OperatorLessNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 112, offset: 1267}, + pos: position{line: 48, col: 112, offset: 1294}, name: "OperatorEqualNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 132, offset: 1287}, + pos: position{line: 48, col: 132, offset: 1314}, name: "OperatorColonNode", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 48, col: 151, offset: 1306}, + pos: position{line: 48, col: 151, offset: 1333}, expr: &litMatcher{ - pos: position{line: 48, col: 151, offset: 1306}, + pos: position{line: 48, col: 151, offset: 1333}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, }, &labeledExpr{ - pos: position{line: 48, col: 156, offset: 1311}, + pos: position{line: 48, col: 156, offset: 1338}, label: "v", expr: &seqExpr{ - pos: position{line: 48, col: 159, offset: 1314}, + pos: position{line: 48, col: 159, offset: 1341}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 48, col: 159, offset: 1314}, + pos: position{line: 48, col: 159, offset: 1341}, name: "FullDate", }, &litMatcher{ - pos: position{line: 48, col: 168, offset: 1323}, + pos: position{line: 48, col: 168, offset: 1350}, val: "T", ignoreCase: false, want: "\"T\"", }, &ruleRefExpr{ - pos: position{line: 48, col: 172, offset: 1327}, + pos: position{line: 48, col: 172, offset: 1354}, name: "FullTime", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 48, col: 182, offset: 1337}, + pos: position{line: 48, col: 182, offset: 1364}, expr: &litMatcher{ - pos: position{line: 48, col: 182, offset: 1337}, + pos: position{line: 48, col: 182, offset: 1364}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -347,51 +358,51 @@ var g = &grammar{ }, { name: "TextPropertyRestrictionNode", - pos: position{line: 52, col: 1, offset: 1408}, + pos: position{line: 52, col: 1, offset: 1435}, expr: &actionExpr{ - pos: position{line: 53, col: 5, offset: 1443}, + pos: position{line: 53, col: 5, offset: 1470}, run: (*parser).callonTextPropertyRestrictionNode1, expr: &seqExpr{ - pos: position{line: 53, col: 5, offset: 1443}, + pos: position{line: 53, col: 5, offset: 1470}, exprs: []any{ &labeledExpr{ - pos: position{line: 53, col: 5, offset: 1443}, + pos: position{line: 53, col: 5, offset: 1470}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 53, col: 7, offset: 1445}, + pos: position{line: 53, col: 7, offset: 1472}, expr: &ruleRefExpr{ - pos: position{line: 53, col: 7, offset: 1445}, + pos: position{line: 53, col: 7, offset: 1472}, name: "Char", }, }, }, &choiceExpr{ - pos: position{line: 53, col: 14, offset: 1452}, + pos: position{line: 53, col: 14, offset: 1479}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 53, col: 14, offset: 1452}, + pos: position{line: 53, col: 14, offset: 1479}, name: "OperatorColonNode", }, &ruleRefExpr{ - pos: position{line: 53, col: 34, offset: 1472}, + pos: position{line: 53, col: 34, offset: 1499}, name: "OperatorEqualNode", }, }, }, &labeledExpr{ - pos: position{line: 53, col: 53, offset: 1491}, + pos: position{line: 53, col: 53, offset: 1518}, label: "v", expr: &choiceExpr{ - pos: position{line: 53, col: 56, offset: 1494}, + pos: position{line: 53, col: 56, offset: 1521}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 53, col: 56, offset: 1494}, + pos: position{line: 53, col: 56, offset: 1521}, name: "String", }, &oneOrMoreExpr{ - pos: position{line: 53, col: 65, offset: 1503}, + pos: position{line: 53, col: 65, offset: 1530}, expr: &charClassMatcher{ - pos: position{line: 53, col: 65, offset: 1503}, + pos: position{line: 53, col: 65, offset: 1530}, val: "[^ ()]", chars: []rune{' ', '(', ')'}, ignoreCase: false, @@ -407,16 +418,16 @@ var g = &grammar{ }, { name: "FreeTextKeywordNodes", - pos: position{line: 61, col: 1, offset: 1709}, + pos: position{line: 61, col: 1, offset: 1736}, expr: &choiceExpr{ - pos: position{line: 62, col: 5, offset: 1737}, + pos: position{line: 62, col: 5, offset: 1764}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 62, col: 5, offset: 1737}, + pos: position{line: 62, col: 5, offset: 1764}, name: "PhraseNode", }, &ruleRefExpr{ - pos: position{line: 63, col: 5, offset: 1754}, + pos: position{line: 63, col: 5, offset: 1781}, name: "WordNode", }, }, @@ -424,40 +435,40 @@ var g = &grammar{ }, { name: "PhraseNode", - pos: position{line: 65, col: 1, offset: 1764}, + pos: position{line: 65, col: 1, offset: 1791}, expr: &actionExpr{ - pos: position{line: 66, col: 6, offset: 1783}, + pos: position{line: 66, col: 6, offset: 1810}, run: (*parser).callonPhraseNode1, expr: &seqExpr{ - pos: position{line: 66, col: 6, offset: 1783}, + pos: position{line: 66, col: 6, offset: 1810}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 66, col: 6, offset: 1783}, + pos: position{line: 66, col: 6, offset: 1810}, expr: &ruleRefExpr{ - pos: position{line: 66, col: 6, offset: 1783}, + pos: position{line: 66, col: 6, offset: 1810}, name: "OperatorColonNode", }, }, &ruleRefExpr{ - pos: position{line: 66, col: 25, offset: 1802}, + pos: position{line: 66, col: 25, offset: 1829}, name: "_", }, &labeledExpr{ - pos: position{line: 66, col: 27, offset: 1804}, + pos: position{line: 66, col: 27, offset: 1831}, label: "v", expr: &ruleRefExpr{ - pos: position{line: 66, col: 29, offset: 1806}, + pos: position{line: 66, col: 29, offset: 1833}, name: "String", }, }, &ruleRefExpr{ - pos: position{line: 66, col: 36, offset: 1813}, + pos: position{line: 66, col: 36, offset: 1840}, name: "_", }, &zeroOrOneExpr{ - pos: position{line: 66, col: 38, offset: 1815}, + pos: position{line: 66, col: 38, offset: 1842}, expr: &ruleRefExpr{ - pos: position{line: 66, col: 38, offset: 1815}, + pos: position{line: 66, col: 38, offset: 1842}, name: "OperatorColonNode", }, }, @@ -467,31 +478,31 @@ var g = &grammar{ }, { name: "WordNode", - pos: position{line: 70, col: 1, offset: 1896}, + pos: position{line: 70, col: 1, offset: 1923}, expr: &actionExpr{ - pos: position{line: 71, col: 6, offset: 1913}, + pos: position{line: 71, col: 6, offset: 1940}, run: (*parser).callonWordNode1, expr: &seqExpr{ - pos: position{line: 71, col: 6, offset: 1913}, + pos: position{line: 71, col: 6, offset: 1940}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 71, col: 6, offset: 1913}, + pos: position{line: 71, col: 6, offset: 1940}, expr: &ruleRefExpr{ - pos: position{line: 71, col: 6, offset: 1913}, + pos: position{line: 71, col: 6, offset: 1940}, name: "OperatorColonNode", }, }, &ruleRefExpr{ - pos: position{line: 71, col: 25, offset: 1932}, + pos: position{line: 71, col: 25, offset: 1959}, name: "_", }, &labeledExpr{ - pos: position{line: 71, col: 27, offset: 1934}, + pos: position{line: 71, col: 27, offset: 1961}, label: "v", expr: &oneOrMoreExpr{ - pos: position{line: 71, col: 29, offset: 1936}, + pos: position{line: 71, col: 29, offset: 1963}, expr: &charClassMatcher{ - pos: position{line: 71, col: 29, offset: 1936}, + pos: position{line: 71, col: 29, offset: 1963}, val: "[^ :()]", chars: []rune{' ', ':', '(', ')'}, ignoreCase: false, @@ -500,13 +511,13 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 71, col: 38, offset: 1945}, + pos: position{line: 71, col: 38, offset: 1972}, name: "_", }, &zeroOrOneExpr{ - pos: position{line: 71, col: 40, offset: 1947}, + pos: position{line: 71, col: 40, offset: 1974}, expr: &ruleRefExpr{ - pos: position{line: 71, col: 40, offset: 1947}, + pos: position{line: 71, col: 40, offset: 1974}, name: "OperatorColonNode", }, }, @@ -515,40 +526,68 @@ var g = &grammar{ }, }, { - name: "OperatorBooleanNode", - pos: position{line: 79, col: 1, offset: 2156}, + name: "OperatorBooleanNodes", + pos: position{line: 79, col: 1, offset: 2183}, + expr: &choiceExpr{ + pos: position{line: 80, col: 5, offset: 2211}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 80, col: 5, offset: 2211}, + name: "OperatorBooleanAndNode", + }, + &ruleRefExpr{ + pos: position{line: 81, col: 5, offset: 2240}, + name: "OperatorBooleanNotNode", + }, + &ruleRefExpr{ + pos: position{line: 82, col: 5, offset: 2269}, + name: "OperatorBooleanOrNode", + }, + }, + }, + }, + { + name: "OperatorBooleanAndNode", + pos: position{line: 84, col: 1, offset: 2292}, expr: &actionExpr{ - pos: position{line: 80, col: 5, offset: 2183}, - run: (*parser).callonOperatorBooleanNode1, + pos: position{line: 85, col: 5, offset: 2322}, + run: (*parser).callonOperatorBooleanAndNode1, expr: &choiceExpr{ - pos: position{line: 80, col: 6, offset: 2184}, + pos: position{line: 85, col: 6, offset: 2323}, alternatives: []any{ &litMatcher{ - pos: position{line: 80, col: 6, offset: 2184}, + pos: position{line: 85, col: 6, offset: 2323}, val: "AND", ignoreCase: false, want: "\"AND\"", }, &litMatcher{ - pos: position{line: 80, col: 14, offset: 2192}, - val: "OR", + pos: position{line: 85, col: 14, offset: 2331}, + val: "+", ignoreCase: false, - want: "\"OR\"", + want: "\"+\"", }, + }, + }, + }, + }, + { + name: "OperatorBooleanNotNode", + pos: position{line: 89, col: 1, offset: 2393}, + expr: &actionExpr{ + pos: position{line: 90, col: 5, offset: 2423}, + run: (*parser).callonOperatorBooleanNotNode1, + expr: &choiceExpr{ + pos: position{line: 90, col: 6, offset: 2424}, + alternatives: []any{ &litMatcher{ - pos: position{line: 80, col: 21, offset: 2199}, + pos: position{line: 90, col: 6, offset: 2424}, val: "NOT", ignoreCase: false, want: "\"NOT\"", }, &litMatcher{ - pos: position{line: 80, col: 29, offset: 2207}, - val: "+", - ignoreCase: false, - want: "\"+\"", - }, - &litMatcher{ - pos: position{line: 80, col: 35, offset: 2213}, + pos: position{line: 90, col: 14, offset: 2432}, val: "-", ignoreCase: false, want: "\"-\"", @@ -557,14 +596,28 @@ var g = &grammar{ }, }, }, + { + name: "OperatorBooleanOrNode", + pos: position{line: 94, col: 1, offset: 2494}, + expr: &actionExpr{ + pos: position{line: 95, col: 5, offset: 2523}, + run: (*parser).callonOperatorBooleanOrNode1, + expr: &litMatcher{ + pos: position{line: 95, col: 6, offset: 2524}, + val: "OR", + ignoreCase: false, + want: "\"OR\"", + }, + }, + }, { name: "OperatorColonNode", - pos: position{line: 84, col: 1, offset: 2275}, + pos: position{line: 99, col: 1, offset: 2587}, expr: &actionExpr{ - pos: position{line: 85, col: 5, offset: 2300}, + pos: position{line: 100, col: 5, offset: 2612}, run: (*parser).callonOperatorColonNode1, expr: &litMatcher{ - pos: position{line: 85, col: 5, offset: 2300}, + pos: position{line: 100, col: 5, offset: 2612}, val: ":", ignoreCase: false, want: "\":\"", @@ -573,12 +626,12 @@ var g = &grammar{ }, { name: "OperatorEqualNode", - pos: position{line: 89, col: 1, offset: 2361}, + pos: position{line: 104, col: 1, offset: 2673}, expr: &actionExpr{ - pos: position{line: 90, col: 5, offset: 2386}, + pos: position{line: 105, col: 5, offset: 2698}, run: (*parser).callonOperatorEqualNode1, expr: &litMatcher{ - pos: position{line: 90, col: 5, offset: 2386}, + pos: position{line: 105, col: 5, offset: 2698}, val: "=", ignoreCase: false, want: "\"=\"", @@ -587,12 +640,12 @@ var g = &grammar{ }, { name: "OperatorLessNode", - pos: position{line: 94, col: 1, offset: 2447}, + pos: position{line: 109, col: 1, offset: 2759}, expr: &actionExpr{ - pos: position{line: 95, col: 5, offset: 2471}, + pos: position{line: 110, col: 5, offset: 2783}, run: (*parser).callonOperatorLessNode1, expr: &litMatcher{ - pos: position{line: 95, col: 5, offset: 2471}, + pos: position{line: 110, col: 5, offset: 2783}, val: "<", ignoreCase: false, want: "\"<\"", @@ -601,12 +654,12 @@ var g = &grammar{ }, { name: "OperatorLessOrEqualNode", - pos: position{line: 99, col: 1, offset: 2532}, + pos: position{line: 114, col: 1, offset: 2844}, expr: &actionExpr{ - pos: position{line: 100, col: 5, offset: 2563}, + pos: position{line: 115, col: 5, offset: 2875}, run: (*parser).callonOperatorLessOrEqualNode1, expr: &litMatcher{ - pos: position{line: 100, col: 5, offset: 2563}, + pos: position{line: 115, col: 5, offset: 2875}, val: "<=", ignoreCase: false, want: "\"<=\"", @@ -615,12 +668,12 @@ var g = &grammar{ }, { name: "OperatorGreaterNode", - pos: position{line: 104, col: 1, offset: 2625}, + pos: position{line: 119, col: 1, offset: 2937}, expr: &actionExpr{ - pos: position{line: 105, col: 5, offset: 2652}, + pos: position{line: 120, col: 5, offset: 2964}, run: (*parser).callonOperatorGreaterNode1, expr: &litMatcher{ - pos: position{line: 105, col: 5, offset: 2652}, + pos: position{line: 120, col: 5, offset: 2964}, val: ">", ignoreCase: false, want: "\">\"", @@ -629,12 +682,12 @@ var g = &grammar{ }, { name: "OperatorGreaterOrEqualNode", - pos: position{line: 109, col: 1, offset: 2713}, + pos: position{line: 124, col: 1, offset: 3025}, expr: &actionExpr{ - pos: position{line: 110, col: 5, offset: 2747}, + pos: position{line: 125, col: 5, offset: 3059}, run: (*parser).callonOperatorGreaterOrEqualNode1, expr: &litMatcher{ - pos: position{line: 110, col: 5, offset: 2747}, + pos: position{line: 125, col: 5, offset: 3059}, val: ">=", ignoreCase: false, want: "\">=\"", @@ -643,27 +696,27 @@ var g = &grammar{ }, { name: "TimeYear", - pos: position{line: 119, col: 1, offset: 2933}, + pos: position{line: 134, col: 1, offset: 3245}, expr: &actionExpr{ - pos: position{line: 120, col: 5, offset: 2949}, + pos: position{line: 135, col: 5, offset: 3261}, run: (*parser).callonTimeYear1, expr: &seqExpr{ - pos: position{line: 120, col: 5, offset: 2949}, + pos: position{line: 135, col: 5, offset: 3261}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 120, col: 5, offset: 2949}, + pos: position{line: 135, col: 5, offset: 3261}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 120, col: 11, offset: 2955}, + pos: position{line: 135, col: 11, offset: 3267}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 120, col: 17, offset: 2961}, + pos: position{line: 135, col: 17, offset: 3273}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 120, col: 23, offset: 2967}, + pos: position{line: 135, col: 23, offset: 3279}, name: "Digit", }, }, @@ -672,19 +725,19 @@ var g = &grammar{ }, { name: "TimeMonth", - pos: position{line: 124, col: 1, offset: 3009}, + pos: position{line: 139, col: 1, offset: 3321}, expr: &actionExpr{ - pos: position{line: 125, col: 5, offset: 3026}, + pos: position{line: 140, col: 5, offset: 3338}, run: (*parser).callonTimeMonth1, expr: &seqExpr{ - pos: position{line: 125, col: 5, offset: 3026}, + pos: position{line: 140, col: 5, offset: 3338}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 125, col: 5, offset: 3026}, + pos: position{line: 140, col: 5, offset: 3338}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 125, col: 11, offset: 3032}, + pos: position{line: 140, col: 11, offset: 3344}, name: "Digit", }, }, @@ -693,19 +746,19 @@ var g = &grammar{ }, { name: "TimeDay", - pos: position{line: 129, col: 1, offset: 3074}, + pos: position{line: 144, col: 1, offset: 3386}, expr: &actionExpr{ - pos: position{line: 130, col: 5, offset: 3089}, + pos: position{line: 145, col: 5, offset: 3401}, run: (*parser).callonTimeDay1, expr: &seqExpr{ - pos: position{line: 130, col: 5, offset: 3089}, + pos: position{line: 145, col: 5, offset: 3401}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 130, col: 5, offset: 3089}, + pos: position{line: 145, col: 5, offset: 3401}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 130, col: 11, offset: 3095}, + pos: position{line: 145, col: 11, offset: 3407}, name: "Digit", }, }, @@ -714,19 +767,19 @@ var g = &grammar{ }, { name: "TimeHour", - pos: position{line: 134, col: 1, offset: 3137}, + pos: position{line: 149, col: 1, offset: 3449}, expr: &actionExpr{ - pos: position{line: 135, col: 5, offset: 3153}, + pos: position{line: 150, col: 5, offset: 3465}, run: (*parser).callonTimeHour1, expr: &seqExpr{ - pos: position{line: 135, col: 5, offset: 3153}, + pos: position{line: 150, col: 5, offset: 3465}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 135, col: 5, offset: 3153}, + pos: position{line: 150, col: 5, offset: 3465}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 135, col: 11, offset: 3159}, + pos: position{line: 150, col: 11, offset: 3471}, name: "Digit", }, }, @@ -735,19 +788,19 @@ var g = &grammar{ }, { name: "TimeMinute", - pos: position{line: 139, col: 1, offset: 3201}, + pos: position{line: 154, col: 1, offset: 3513}, expr: &actionExpr{ - pos: position{line: 140, col: 5, offset: 3219}, + pos: position{line: 155, col: 5, offset: 3531}, run: (*parser).callonTimeMinute1, expr: &seqExpr{ - pos: position{line: 140, col: 5, offset: 3219}, + pos: position{line: 155, col: 5, offset: 3531}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 140, col: 5, offset: 3219}, + pos: position{line: 155, col: 5, offset: 3531}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 140, col: 11, offset: 3225}, + pos: position{line: 155, col: 11, offset: 3537}, name: "Digit", }, }, @@ -756,19 +809,19 @@ var g = &grammar{ }, { name: "TimeSecond", - pos: position{line: 144, col: 1, offset: 3267}, + pos: position{line: 159, col: 1, offset: 3579}, expr: &actionExpr{ - pos: position{line: 145, col: 5, offset: 3285}, + pos: position{line: 160, col: 5, offset: 3597}, run: (*parser).callonTimeSecond1, expr: &seqExpr{ - pos: position{line: 145, col: 5, offset: 3285}, + pos: position{line: 160, col: 5, offset: 3597}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 145, col: 5, offset: 3285}, + pos: position{line: 160, col: 5, offset: 3597}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 145, col: 11, offset: 3291}, + pos: position{line: 160, col: 11, offset: 3603}, name: "Digit", }, }, @@ -777,35 +830,35 @@ var g = &grammar{ }, { name: "FullDate", - pos: position{line: 149, col: 1, offset: 3333}, + pos: position{line: 164, col: 1, offset: 3645}, expr: &actionExpr{ - pos: position{line: 150, col: 5, offset: 3349}, + pos: position{line: 165, col: 5, offset: 3661}, run: (*parser).callonFullDate1, expr: &seqExpr{ - pos: position{line: 150, col: 5, offset: 3349}, + pos: position{line: 165, col: 5, offset: 3661}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 150, col: 5, offset: 3349}, + pos: position{line: 165, col: 5, offset: 3661}, name: "TimeYear", }, &litMatcher{ - pos: position{line: 150, col: 14, offset: 3358}, + pos: position{line: 165, col: 14, offset: 3670}, val: "-", ignoreCase: false, want: "\"-\"", }, &ruleRefExpr{ - pos: position{line: 150, col: 18, offset: 3362}, + pos: position{line: 165, col: 18, offset: 3674}, name: "TimeMonth", }, &litMatcher{ - pos: position{line: 150, col: 28, offset: 3372}, + pos: position{line: 165, col: 28, offset: 3684}, val: "-", ignoreCase: false, want: "\"-\"", }, &ruleRefExpr{ - pos: position{line: 150, col: 32, offset: 3376}, + pos: position{line: 165, col: 32, offset: 3688}, name: "TimeDay", }, }, @@ -814,52 +867,52 @@ var g = &grammar{ }, { name: "FullTime", - pos: position{line: 154, col: 1, offset: 3420}, + pos: position{line: 169, col: 1, offset: 3732}, expr: &actionExpr{ - pos: position{line: 155, col: 5, offset: 3436}, + pos: position{line: 170, col: 5, offset: 3748}, run: (*parser).callonFullTime1, expr: &seqExpr{ - pos: position{line: 155, col: 5, offset: 3436}, + pos: position{line: 170, col: 5, offset: 3748}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 155, col: 5, offset: 3436}, + pos: position{line: 170, col: 5, offset: 3748}, name: "TimeHour", }, &litMatcher{ - pos: position{line: 155, col: 14, offset: 3445}, + pos: position{line: 170, col: 14, offset: 3757}, val: ":", ignoreCase: false, want: "\":\"", }, &ruleRefExpr{ - pos: position{line: 155, col: 18, offset: 3449}, + pos: position{line: 170, col: 18, offset: 3761}, name: "TimeMinute", }, &litMatcher{ - pos: position{line: 155, col: 29, offset: 3460}, + pos: position{line: 170, col: 29, offset: 3772}, val: ":", ignoreCase: false, want: "\":\"", }, &ruleRefExpr{ - pos: position{line: 155, col: 33, offset: 3464}, + pos: position{line: 170, col: 33, offset: 3776}, name: "TimeSecond", }, &zeroOrOneExpr{ - pos: position{line: 155, col: 44, offset: 3475}, + pos: position{line: 170, col: 44, offset: 3787}, expr: &seqExpr{ - pos: position{line: 155, col: 45, offset: 3476}, + pos: position{line: 170, col: 45, offset: 3788}, exprs: []any{ &litMatcher{ - pos: position{line: 155, col: 45, offset: 3476}, + pos: position{line: 170, col: 45, offset: 3788}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 155, col: 49, offset: 3480}, + pos: position{line: 170, col: 49, offset: 3792}, expr: &ruleRefExpr{ - pos: position{line: 155, col: 49, offset: 3480}, + pos: position{line: 170, col: 49, offset: 3792}, name: "Digit", }, }, @@ -867,28 +920,28 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 155, col: 59, offset: 3490}, + pos: position{line: 170, col: 59, offset: 3802}, alternatives: []any{ &litMatcher{ - pos: position{line: 155, col: 59, offset: 3490}, + pos: position{line: 170, col: 59, offset: 3802}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 155, col: 65, offset: 3496}, + pos: position{line: 170, col: 65, offset: 3808}, exprs: []any{ &choiceExpr{ - pos: position{line: 155, col: 66, offset: 3497}, + pos: position{line: 170, col: 66, offset: 3809}, alternatives: []any{ &litMatcher{ - pos: position{line: 155, col: 66, offset: 3497}, + pos: position{line: 170, col: 66, offset: 3809}, val: "+", ignoreCase: false, want: "\"+\"", }, &litMatcher{ - pos: position{line: 155, col: 72, offset: 3503}, + pos: position{line: 170, col: 72, offset: 3815}, val: "-", ignoreCase: false, want: "\"-\"", @@ -896,17 +949,17 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 155, col: 77, offset: 3508}, + pos: position{line: 170, col: 77, offset: 3820}, name: "TimeHour", }, &litMatcher{ - pos: position{line: 155, col: 86, offset: 3517}, + pos: position{line: 170, col: 86, offset: 3829}, val: ":", ignoreCase: false, want: "\":\"", }, &ruleRefExpr{ - pos: position{line: 155, col: 90, offset: 3521}, + pos: position{line: 170, col: 90, offset: 3833}, name: "TimeMinute", }, }, @@ -919,12 +972,12 @@ var g = &grammar{ }, { name: "Char", - pos: position{line: 163, col: 1, offset: 3692}, + pos: position{line: 178, col: 1, offset: 4004}, expr: &actionExpr{ - pos: position{line: 164, col: 5, offset: 3704}, + pos: position{line: 179, col: 5, offset: 4016}, run: (*parser).callonChar1, expr: &charClassMatcher{ - pos: position{line: 164, col: 5, offset: 3704}, + pos: position{line: 179, col: 5, offset: 4016}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -934,26 +987,26 @@ var g = &grammar{ }, { name: "String", - pos: position{line: 168, col: 1, offset: 3749}, + pos: position{line: 183, col: 1, offset: 4061}, expr: &actionExpr{ - pos: position{line: 169, col: 5, offset: 3763}, + pos: position{line: 184, col: 5, offset: 4075}, run: (*parser).callonString1, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3763}, + pos: position{line: 184, col: 5, offset: 4075}, exprs: []any{ &litMatcher{ - pos: position{line: 169, col: 5, offset: 3763}, + pos: position{line: 184, col: 5, offset: 4075}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 169, col: 9, offset: 3767}, + pos: position{line: 184, col: 9, offset: 4079}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 169, col: 11, offset: 3769}, + pos: position{line: 184, col: 11, offset: 4081}, expr: &charClassMatcher{ - pos: position{line: 169, col: 11, offset: 3769}, + pos: position{line: 184, col: 11, offset: 4081}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -962,7 +1015,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 169, col: 17, offset: 3775}, + pos: position{line: 184, col: 17, offset: 4087}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -973,12 +1026,12 @@ var g = &grammar{ }, { name: "Digit", - pos: position{line: 173, col: 1, offset: 3810}, + pos: position{line: 188, col: 1, offset: 4122}, expr: &actionExpr{ - pos: position{line: 174, col: 5, offset: 3823}, + pos: position{line: 189, col: 5, offset: 4135}, run: (*parser).callonDigit1, expr: &charClassMatcher{ - pos: position{line: 174, col: 5, offset: 3823}, + pos: position{line: 189, col: 5, offset: 4135}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -988,11 +1041,11 @@ var g = &grammar{ }, { name: "_", - pos: position{line: 178, col: 1, offset: 3865}, + pos: position{line: 193, col: 1, offset: 4177}, expr: &zeroOrMoreExpr{ - pos: position{line: 179, col: 5, offset: 3874}, + pos: position{line: 194, col: 5, offset: 4186}, expr: &charClassMatcher{ - pos: position{line: 179, col: 5, offset: 3874}, + pos: position{line: 194, col: 5, offset: 4186}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1091,15 +1144,37 @@ func (p *parser) callonWordNode1() (any, error) { return p.cur.onWordNode1(stack["v"]) } -func (c *current) onOperatorBooleanNode1() (any, error) { +func (c *current) onOperatorBooleanAndNode1() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonOperatorBooleanAndNode1() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onOperatorBooleanAndNode1() +} + +func (c *current) onOperatorBooleanNotNode1() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonOperatorBooleanNotNode1() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onOperatorBooleanNotNode1() +} + +func (c *current) onOperatorBooleanOrNode1() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonOperatorBooleanNode1() (any, error) { +func (p *parser) callonOperatorBooleanOrNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorBooleanNode1() + return p.cur.onOperatorBooleanOrNode1() } func (c *current) onOperatorColonNode1() (any, error) { diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index e60645f4ab1..faafbf19038 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -1,6 +1,7 @@ package kql_test import ( + "errors" "strings" "testing" "time" @@ -63,6 +64,25 @@ func TestParse(t *testing.T) { // SPEC ////////////////////////////////////////////////////////////////////////////// // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf // + // 2.1.2 AND Operator + { + name: `cat AND dog`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + }, + { + name: `AND`, + expectedError: errors.New(""), + }, + { + name: `AND cat AND dog`, + expectedError: errors.New(""), + }, // 3.1.11 Implicit Operator { name: `cat dog`, @@ -727,11 +747,14 @@ func TestParse(t *testing.T) { q = tt.givenQuery } - parsedAST, err := kql.Parse("", []byte(q)) + parsedAST, err := kql.Builder{}.Build(q) if tt.expectedError != nil { - assert.Equal(err, tt.expectedError) - assert.Nil(parsedAST) + if tt.expectedError.Error() != "" { + assert.Equal(err, tt.expectedError) + } else { + assert.NotNil(err) + } return } diff --git a/services/search/pkg/query/kql/kql.go b/services/search/pkg/query/kql/kql.go index 0566278aa62..48c349e817c 100644 --- a/services/search/pkg/query/kql/kql.go +++ b/services/search/pkg/query/kql/kql.go @@ -2,6 +2,7 @@ package kql import ( + "errors" "strings" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" @@ -24,8 +25,18 @@ type Builder struct{} func (b Builder) Build(q string) (*ast.Ast, error) { f, err := Parse("", []byte(q)) if err != nil { - return nil, err + var list errList + errors.As(err, &list) + + for _, listError := range list { + var parserError *parserError + switch { + case errors.As(listError, &parserError): + return nil, listError + } + } } + return f.(*ast.Ast), nil } diff --git a/services/search/pkg/query/kql/kql_test.go b/services/search/pkg/query/kql/kql_test.go index d18068ed575..0e8e871221f 100644 --- a/services/search/pkg/query/kql/kql_test.go +++ b/services/search/pkg/query/kql/kql_test.go @@ -21,7 +21,7 @@ func TestNewAST(t *testing.T) { { name: "error", givenQuery: kql.BoolAND, - shouldError: false, + shouldError: true, }, } From d9bc5689a261f70693563f2d79b196bb4b4ac201 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Fri, 8 Sep 2023 14:03:02 +0200 Subject: [PATCH 03/11] enhancement: add kql docs and support for date and time only dateTimeRestriction queries --- services/search/pkg/query/kql/cast.go | 4 +- services/search/pkg/query/kql/dictionary.peg | 23 +- .../search/pkg/query/kql/dictionary_gen.go | 525 ++++++++++-------- .../search/pkg/query/kql/dictionary_test.go | 87 ++- services/search/pkg/query/kql/doc.go | 1 - 5 files changed, 395 insertions(+), 245 deletions(-) diff --git a/services/search/pkg/query/kql/cast.go b/services/search/pkg/query/kql/cast.go index 41c8183a62a..2f7c58c3531 100644 --- a/services/search/pkg/query/kql/cast.go +++ b/services/search/pkg/query/kql/cast.go @@ -4,6 +4,8 @@ import ( "fmt" "time" + "github.com/araddon/dateparse" + "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -56,5 +58,5 @@ func toTime(in interface{}) (time.Time, error) { return time.Time{}, err } - return time.Parse(time.RFC3339Nano, ts) + return dateparse.ParseLocal(ts) } diff --git a/services/search/pkg/query/kql/dictionary.peg b/services/search/pkg/query/kql/dictionary.peg index 289d3e4bdb8..df8784d807c 100644 --- a/services/search/pkg/query/kql/dictionary.peg +++ b/services/search/pkg/query/kql/dictionary.peg @@ -7,7 +7,10 @@ //////////////////////////////////////////////////////// AST <- - _ !OperatorBooleanAndNode _ nodes:Nodes _ { + _ !( + OperatorBooleanAndNode / + OperatorBooleanOrNode + ) _ nodes:Nodes _ { return buildAST(nodes, c.text, c.pos) } @@ -45,7 +48,18 @@ YesNoPropertyRestrictionNode <- } DateTimeRestrictionNode <- - k:Char+ o:(OperatorGreaterOrEqualNode / OperatorLessOrEqualNode / OperatorGreaterNode / OperatorLessNode / OperatorEqualNode / OperatorColonNode) '"'? v:(FullDate "T" FullTime) '"'? { + k:Char+ o:( + OperatorGreaterOrEqualNode / + OperatorLessOrEqualNode / + OperatorGreaterNode / + OperatorLessNode / + OperatorEqualNode / + OperatorColonNode + ) '"'? v:( + DateTime / + FullDate / + FullTime + ) '"'? { return buildDateTimeNode(k, o, v, c.text, c.pos) } @@ -171,6 +185,11 @@ FullTime <- return c.text, nil } +DateTime + = FullDate "T" FullTime { + return c.text, nil + } + //////////////////////////////////////////////////////// // misc //////////////////////////////////////////////////////// diff --git a/services/search/pkg/query/kql/dictionary_gen.go b/services/search/pkg/query/kql/dictionary_gen.go index 67c9959e1c9..8f83d73d105 100644 --- a/services/search/pkg/query/kql/dictionary_gen.go +++ b/services/search/pkg/query/kql/dictionary_gen.go @@ -34,25 +34,34 @@ var g = &grammar{ }, ¬Expr{ pos: position{line: 10, col: 7, offset: 156}, - expr: &ruleRefExpr{ - pos: position{line: 10, col: 8, offset: 157}, - name: "OperatorBooleanAndNode", + expr: &choiceExpr{ + pos: position{line: 11, col: 9, offset: 167}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 11, col: 9, offset: 167}, + name: "OperatorBooleanAndNode", + }, + &ruleRefExpr{ + pos: position{line: 12, col: 9, offset: 200}, + name: "OperatorBooleanOrNode", + }, + }, }, }, &ruleRefExpr{ - pos: position{line: 10, col: 31, offset: 180}, + pos: position{line: 13, col: 7, offset: 228}, name: "_", }, &labeledExpr{ - pos: position{line: 10, col: 33, offset: 182}, + pos: position{line: 13, col: 9, offset: 230}, label: "nodes", expr: &ruleRefExpr{ - pos: position{line: 10, col: 39, offset: 188}, + pos: position{line: 13, col: 15, offset: 236}, name: "Nodes", }, }, &ruleRefExpr{ - pos: position{line: 10, col: 45, offset: 194}, + pos: position{line: 13, col: 21, offset: 242}, name: "_", }, }, @@ -61,53 +70,53 @@ var g = &grammar{ }, { name: "Nodes", - pos: position{line: 14, col: 1, offset: 251}, + pos: position{line: 17, col: 1, offset: 299}, expr: &actionExpr{ - pos: position{line: 15, col: 5, offset: 264}, + pos: position{line: 18, col: 5, offset: 312}, run: (*parser).callonNodes1, expr: &seqExpr{ - pos: position{line: 15, col: 5, offset: 264}, + pos: position{line: 18, col: 5, offset: 312}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 15, col: 5, offset: 264}, + pos: position{line: 18, col: 5, offset: 312}, name: "_", }, &labeledExpr{ - pos: position{line: 15, col: 7, offset: 266}, + pos: position{line: 18, col: 7, offset: 314}, label: "head", expr: &choiceExpr{ - pos: position{line: 16, col: 9, offset: 281}, + pos: position{line: 19, col: 9, offset: 329}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 16, col: 9, offset: 281}, + pos: position{line: 19, col: 9, offset: 329}, name: "GroupNode", }, &ruleRefExpr{ - pos: position{line: 17, col: 9, offset: 301}, + pos: position{line: 20, col: 9, offset: 349}, name: "PropertyRestrictionNodes", }, &ruleRefExpr{ - pos: position{line: 18, col: 9, offset: 336}, + pos: position{line: 21, col: 9, offset: 384}, name: "OperatorBooleanNodes", }, &ruleRefExpr{ - pos: position{line: 19, col: 9, offset: 367}, + pos: position{line: 22, col: 9, offset: 415}, name: "FreeTextKeywordNodes", }, }, }, }, &ruleRefExpr{ - pos: position{line: 20, col: 7, offset: 394}, + pos: position{line: 23, col: 7, offset: 442}, name: "_", }, &labeledExpr{ - pos: position{line: 20, col: 9, offset: 396}, + pos: position{line: 23, col: 9, offset: 444}, label: "tail", expr: &zeroOrOneExpr{ - pos: position{line: 20, col: 14, offset: 401}, + pos: position{line: 23, col: 14, offset: 449}, expr: &ruleRefExpr{ - pos: position{line: 20, col: 14, offset: 401}, + pos: position{line: 23, col: 14, offset: 449}, name: "Nodes", }, }, @@ -118,59 +127,59 @@ var g = &grammar{ }, { name: "GroupNode", - pos: position{line: 28, col: 1, offset: 579}, + pos: position{line: 31, col: 1, offset: 627}, expr: &actionExpr{ - pos: position{line: 29, col: 5, offset: 596}, + pos: position{line: 32, col: 5, offset: 644}, run: (*parser).callonGroupNode1, expr: &seqExpr{ - pos: position{line: 29, col: 5, offset: 596}, + pos: position{line: 32, col: 5, offset: 644}, exprs: []any{ &labeledExpr{ - pos: position{line: 29, col: 5, offset: 596}, + pos: position{line: 32, col: 5, offset: 644}, label: "k", expr: &zeroOrOneExpr{ - pos: position{line: 29, col: 7, offset: 598}, + pos: position{line: 32, col: 7, offset: 646}, expr: &oneOrMoreExpr{ - pos: position{line: 29, col: 8, offset: 599}, + pos: position{line: 32, col: 8, offset: 647}, expr: &ruleRefExpr{ - pos: position{line: 29, col: 8, offset: 599}, + pos: position{line: 32, col: 8, offset: 647}, name: "Char", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 29, col: 16, offset: 607}, + pos: position{line: 32, col: 16, offset: 655}, expr: &choiceExpr{ - pos: position{line: 29, col: 17, offset: 608}, + pos: position{line: 32, col: 17, offset: 656}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 29, col: 17, offset: 608}, + pos: position{line: 32, col: 17, offset: 656}, name: "OperatorColonNode", }, &ruleRefExpr{ - pos: position{line: 29, col: 37, offset: 628}, + pos: position{line: 32, col: 37, offset: 676}, name: "OperatorEqualNode", }, }, }, }, &litMatcher{ - pos: position{line: 29, col: 57, offset: 648}, + pos: position{line: 32, col: 57, offset: 696}, val: "(", ignoreCase: false, want: "\"(\"", }, &labeledExpr{ - pos: position{line: 29, col: 61, offset: 652}, + pos: position{line: 32, col: 61, offset: 700}, label: "v", expr: &ruleRefExpr{ - pos: position{line: 29, col: 63, offset: 654}, + pos: position{line: 32, col: 63, offset: 702}, name: "Nodes", }, }, &litMatcher{ - pos: position{line: 29, col: 69, offset: 660}, + pos: position{line: 32, col: 69, offset: 708}, val: ")", ignoreCase: false, want: "\")\"", @@ -181,20 +190,20 @@ var g = &grammar{ }, { name: "PropertyRestrictionNodes", - pos: position{line: 37, col: 1, offset: 864}, + pos: position{line: 40, col: 1, offset: 912}, expr: &choiceExpr{ - pos: position{line: 38, col: 5, offset: 896}, + pos: position{line: 41, col: 5, offset: 944}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 38, col: 5, offset: 896}, + pos: position{line: 41, col: 5, offset: 944}, name: "YesNoPropertyRestrictionNode", }, &ruleRefExpr{ - pos: position{line: 39, col: 5, offset: 931}, + pos: position{line: 42, col: 5, offset: 979}, name: "DateTimeRestrictionNode", }, &ruleRefExpr{ - pos: position{line: 40, col: 5, offset: 961}, + pos: position{line: 43, col: 5, offset: 1009}, name: "TextPropertyRestrictionNode", }, }, @@ -202,51 +211,51 @@ var g = &grammar{ }, { name: "YesNoPropertyRestrictionNode", - pos: position{line: 42, col: 1, offset: 990}, + pos: position{line: 45, col: 1, offset: 1038}, expr: &actionExpr{ - pos: position{line: 43, col: 5, offset: 1026}, + pos: position{line: 46, col: 5, offset: 1074}, run: (*parser).callonYesNoPropertyRestrictionNode1, expr: &seqExpr{ - pos: position{line: 43, col: 5, offset: 1026}, + pos: position{line: 46, col: 5, offset: 1074}, exprs: []any{ &labeledExpr{ - pos: position{line: 43, col: 5, offset: 1026}, + pos: position{line: 46, col: 5, offset: 1074}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 43, col: 7, offset: 1028}, + pos: position{line: 46, col: 7, offset: 1076}, expr: &ruleRefExpr{ - pos: position{line: 43, col: 7, offset: 1028}, + pos: position{line: 46, col: 7, offset: 1076}, name: "Char", }, }, }, &choiceExpr{ - pos: position{line: 43, col: 14, offset: 1035}, + pos: position{line: 46, col: 14, offset: 1083}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 43, col: 14, offset: 1035}, + pos: position{line: 46, col: 14, offset: 1083}, name: "OperatorColonNode", }, &ruleRefExpr{ - pos: position{line: 43, col: 34, offset: 1055}, + pos: position{line: 46, col: 34, offset: 1103}, name: "OperatorEqualNode", }, }, }, &labeledExpr{ - pos: position{line: 43, col: 53, offset: 1074}, + pos: position{line: 46, col: 53, offset: 1122}, label: "v", expr: &choiceExpr{ - pos: position{line: 43, col: 56, offset: 1077}, + pos: position{line: 46, col: 56, offset: 1125}, alternatives: []any{ &litMatcher{ - pos: position{line: 43, col: 56, offset: 1077}, + pos: position{line: 46, col: 56, offset: 1125}, val: "true", ignoreCase: false, want: "\"true\"", }, &litMatcher{ - pos: position{line: 43, col: 65, offset: 1086}, + pos: position{line: 46, col: 65, offset: 1134}, val: "false", ignoreCase: false, want: "\"false\"", @@ -260,93 +269,91 @@ var g = &grammar{ }, { name: "DateTimeRestrictionNode", - pos: position{line: 47, col: 1, offset: 1156}, + pos: position{line: 50, col: 1, offset: 1204}, expr: &actionExpr{ - pos: position{line: 48, col: 5, offset: 1187}, + pos: position{line: 51, col: 5, offset: 1235}, run: (*parser).callonDateTimeRestrictionNode1, expr: &seqExpr{ - pos: position{line: 48, col: 5, offset: 1187}, + pos: position{line: 51, col: 5, offset: 1235}, exprs: []any{ &labeledExpr{ - pos: position{line: 48, col: 5, offset: 1187}, + pos: position{line: 51, col: 5, offset: 1235}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 48, col: 7, offset: 1189}, + pos: position{line: 51, col: 7, offset: 1237}, expr: &ruleRefExpr{ - pos: position{line: 48, col: 7, offset: 1189}, + pos: position{line: 51, col: 7, offset: 1237}, name: "Char", }, }, }, &labeledExpr{ - pos: position{line: 48, col: 13, offset: 1195}, + pos: position{line: 51, col: 13, offset: 1243}, label: "o", expr: &choiceExpr{ - pos: position{line: 48, col: 16, offset: 1198}, + pos: position{line: 52, col: 9, offset: 1255}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 48, col: 16, offset: 1198}, + pos: position{line: 52, col: 9, offset: 1255}, name: "OperatorGreaterOrEqualNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 45, offset: 1227}, + pos: position{line: 53, col: 9, offset: 1292}, name: "OperatorLessOrEqualNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 71, offset: 1253}, + pos: position{line: 54, col: 9, offset: 1326}, name: "OperatorGreaterNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 93, offset: 1275}, + pos: position{line: 55, col: 9, offset: 1356}, name: "OperatorLessNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 112, offset: 1294}, + pos: position{line: 56, col: 9, offset: 1383}, name: "OperatorEqualNode", }, &ruleRefExpr{ - pos: position{line: 48, col: 132, offset: 1314}, + pos: position{line: 57, col: 9, offset: 1411}, name: "OperatorColonNode", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 48, col: 151, offset: 1333}, + pos: position{line: 58, col: 7, offset: 1435}, expr: &litMatcher{ - pos: position{line: 48, col: 151, offset: 1333}, + pos: position{line: 58, col: 7, offset: 1435}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, }, &labeledExpr{ - pos: position{line: 48, col: 156, offset: 1338}, + pos: position{line: 58, col: 12, offset: 1440}, label: "v", - expr: &seqExpr{ - pos: position{line: 48, col: 159, offset: 1341}, - exprs: []any{ + expr: &choiceExpr{ + pos: position{line: 59, col: 9, offset: 1452}, + alternatives: []any{ &ruleRefExpr{ - pos: position{line: 48, col: 159, offset: 1341}, - name: "FullDate", + pos: position{line: 59, col: 9, offset: 1452}, + name: "DateTime", }, - &litMatcher{ - pos: position{line: 48, col: 168, offset: 1350}, - val: "T", - ignoreCase: false, - want: "\"T\"", + &ruleRefExpr{ + pos: position{line: 60, col: 9, offset: 1471}, + name: "FullDate", }, &ruleRefExpr{ - pos: position{line: 48, col: 172, offset: 1354}, + pos: position{line: 61, col: 9, offset: 1490}, name: "FullTime", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 48, col: 182, offset: 1364}, + pos: position{line: 62, col: 7, offset: 1505}, expr: &litMatcher{ - pos: position{line: 48, col: 182, offset: 1364}, + pos: position{line: 62, col: 7, offset: 1505}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -358,51 +365,51 @@ var g = &grammar{ }, { name: "TextPropertyRestrictionNode", - pos: position{line: 52, col: 1, offset: 1435}, + pos: position{line: 66, col: 1, offset: 1576}, expr: &actionExpr{ - pos: position{line: 53, col: 5, offset: 1470}, + pos: position{line: 67, col: 5, offset: 1611}, run: (*parser).callonTextPropertyRestrictionNode1, expr: &seqExpr{ - pos: position{line: 53, col: 5, offset: 1470}, + pos: position{line: 67, col: 5, offset: 1611}, exprs: []any{ &labeledExpr{ - pos: position{line: 53, col: 5, offset: 1470}, + pos: position{line: 67, col: 5, offset: 1611}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 53, col: 7, offset: 1472}, + pos: position{line: 67, col: 7, offset: 1613}, expr: &ruleRefExpr{ - pos: position{line: 53, col: 7, offset: 1472}, + pos: position{line: 67, col: 7, offset: 1613}, name: "Char", }, }, }, &choiceExpr{ - pos: position{line: 53, col: 14, offset: 1479}, + pos: position{line: 67, col: 14, offset: 1620}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 53, col: 14, offset: 1479}, + pos: position{line: 67, col: 14, offset: 1620}, name: "OperatorColonNode", }, &ruleRefExpr{ - pos: position{line: 53, col: 34, offset: 1499}, + pos: position{line: 67, col: 34, offset: 1640}, name: "OperatorEqualNode", }, }, }, &labeledExpr{ - pos: position{line: 53, col: 53, offset: 1518}, + pos: position{line: 67, col: 53, offset: 1659}, label: "v", expr: &choiceExpr{ - pos: position{line: 53, col: 56, offset: 1521}, + pos: position{line: 67, col: 56, offset: 1662}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 53, col: 56, offset: 1521}, + pos: position{line: 67, col: 56, offset: 1662}, name: "String", }, &oneOrMoreExpr{ - pos: position{line: 53, col: 65, offset: 1530}, + pos: position{line: 67, col: 65, offset: 1671}, expr: &charClassMatcher{ - pos: position{line: 53, col: 65, offset: 1530}, + pos: position{line: 67, col: 65, offset: 1671}, val: "[^ ()]", chars: []rune{' ', '(', ')'}, ignoreCase: false, @@ -418,16 +425,16 @@ var g = &grammar{ }, { name: "FreeTextKeywordNodes", - pos: position{line: 61, col: 1, offset: 1736}, + pos: position{line: 75, col: 1, offset: 1877}, expr: &choiceExpr{ - pos: position{line: 62, col: 5, offset: 1764}, + pos: position{line: 76, col: 5, offset: 1905}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 62, col: 5, offset: 1764}, + pos: position{line: 76, col: 5, offset: 1905}, name: "PhraseNode", }, &ruleRefExpr{ - pos: position{line: 63, col: 5, offset: 1781}, + pos: position{line: 77, col: 5, offset: 1922}, name: "WordNode", }, }, @@ -435,40 +442,40 @@ var g = &grammar{ }, { name: "PhraseNode", - pos: position{line: 65, col: 1, offset: 1791}, + pos: position{line: 79, col: 1, offset: 1932}, expr: &actionExpr{ - pos: position{line: 66, col: 6, offset: 1810}, + pos: position{line: 80, col: 6, offset: 1951}, run: (*parser).callonPhraseNode1, expr: &seqExpr{ - pos: position{line: 66, col: 6, offset: 1810}, + pos: position{line: 80, col: 6, offset: 1951}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 66, col: 6, offset: 1810}, + pos: position{line: 80, col: 6, offset: 1951}, expr: &ruleRefExpr{ - pos: position{line: 66, col: 6, offset: 1810}, + pos: position{line: 80, col: 6, offset: 1951}, name: "OperatorColonNode", }, }, &ruleRefExpr{ - pos: position{line: 66, col: 25, offset: 1829}, + pos: position{line: 80, col: 25, offset: 1970}, name: "_", }, &labeledExpr{ - pos: position{line: 66, col: 27, offset: 1831}, + pos: position{line: 80, col: 27, offset: 1972}, label: "v", expr: &ruleRefExpr{ - pos: position{line: 66, col: 29, offset: 1833}, + pos: position{line: 80, col: 29, offset: 1974}, name: "String", }, }, &ruleRefExpr{ - pos: position{line: 66, col: 36, offset: 1840}, + pos: position{line: 80, col: 36, offset: 1981}, name: "_", }, &zeroOrOneExpr{ - pos: position{line: 66, col: 38, offset: 1842}, + pos: position{line: 80, col: 38, offset: 1983}, expr: &ruleRefExpr{ - pos: position{line: 66, col: 38, offset: 1842}, + pos: position{line: 80, col: 38, offset: 1983}, name: "OperatorColonNode", }, }, @@ -478,31 +485,31 @@ var g = &grammar{ }, { name: "WordNode", - pos: position{line: 70, col: 1, offset: 1923}, + pos: position{line: 84, col: 1, offset: 2064}, expr: &actionExpr{ - pos: position{line: 71, col: 6, offset: 1940}, + pos: position{line: 85, col: 6, offset: 2081}, run: (*parser).callonWordNode1, expr: &seqExpr{ - pos: position{line: 71, col: 6, offset: 1940}, + pos: position{line: 85, col: 6, offset: 2081}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 71, col: 6, offset: 1940}, + pos: position{line: 85, col: 6, offset: 2081}, expr: &ruleRefExpr{ - pos: position{line: 71, col: 6, offset: 1940}, + pos: position{line: 85, col: 6, offset: 2081}, name: "OperatorColonNode", }, }, &ruleRefExpr{ - pos: position{line: 71, col: 25, offset: 1959}, + pos: position{line: 85, col: 25, offset: 2100}, name: "_", }, &labeledExpr{ - pos: position{line: 71, col: 27, offset: 1961}, + pos: position{line: 85, col: 27, offset: 2102}, label: "v", expr: &oneOrMoreExpr{ - pos: position{line: 71, col: 29, offset: 1963}, + pos: position{line: 85, col: 29, offset: 2104}, expr: &charClassMatcher{ - pos: position{line: 71, col: 29, offset: 1963}, + pos: position{line: 85, col: 29, offset: 2104}, val: "[^ :()]", chars: []rune{' ', ':', '(', ')'}, ignoreCase: false, @@ -511,13 +518,13 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 71, col: 38, offset: 1972}, + pos: position{line: 85, col: 38, offset: 2113}, name: "_", }, &zeroOrOneExpr{ - pos: position{line: 71, col: 40, offset: 1974}, + pos: position{line: 85, col: 40, offset: 2115}, expr: &ruleRefExpr{ - pos: position{line: 71, col: 40, offset: 1974}, + pos: position{line: 85, col: 40, offset: 2115}, name: "OperatorColonNode", }, }, @@ -527,20 +534,20 @@ var g = &grammar{ }, { name: "OperatorBooleanNodes", - pos: position{line: 79, col: 1, offset: 2183}, + pos: position{line: 93, col: 1, offset: 2324}, expr: &choiceExpr{ - pos: position{line: 80, col: 5, offset: 2211}, + pos: position{line: 94, col: 5, offset: 2352}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 80, col: 5, offset: 2211}, + pos: position{line: 94, col: 5, offset: 2352}, name: "OperatorBooleanAndNode", }, &ruleRefExpr{ - pos: position{line: 81, col: 5, offset: 2240}, + pos: position{line: 95, col: 5, offset: 2381}, name: "OperatorBooleanNotNode", }, &ruleRefExpr{ - pos: position{line: 82, col: 5, offset: 2269}, + pos: position{line: 96, col: 5, offset: 2410}, name: "OperatorBooleanOrNode", }, }, @@ -548,21 +555,21 @@ var g = &grammar{ }, { name: "OperatorBooleanAndNode", - pos: position{line: 84, col: 1, offset: 2292}, + pos: position{line: 98, col: 1, offset: 2433}, expr: &actionExpr{ - pos: position{line: 85, col: 5, offset: 2322}, + pos: position{line: 99, col: 5, offset: 2463}, run: (*parser).callonOperatorBooleanAndNode1, expr: &choiceExpr{ - pos: position{line: 85, col: 6, offset: 2323}, + pos: position{line: 99, col: 6, offset: 2464}, alternatives: []any{ &litMatcher{ - pos: position{line: 85, col: 6, offset: 2323}, + pos: position{line: 99, col: 6, offset: 2464}, val: "AND", ignoreCase: false, want: "\"AND\"", }, &litMatcher{ - pos: position{line: 85, col: 14, offset: 2331}, + pos: position{line: 99, col: 14, offset: 2472}, val: "+", ignoreCase: false, want: "\"+\"", @@ -573,21 +580,21 @@ var g = &grammar{ }, { name: "OperatorBooleanNotNode", - pos: position{line: 89, col: 1, offset: 2393}, + pos: position{line: 103, col: 1, offset: 2534}, expr: &actionExpr{ - pos: position{line: 90, col: 5, offset: 2423}, + pos: position{line: 104, col: 5, offset: 2564}, run: (*parser).callonOperatorBooleanNotNode1, expr: &choiceExpr{ - pos: position{line: 90, col: 6, offset: 2424}, + pos: position{line: 104, col: 6, offset: 2565}, alternatives: []any{ &litMatcher{ - pos: position{line: 90, col: 6, offset: 2424}, + pos: position{line: 104, col: 6, offset: 2565}, val: "NOT", ignoreCase: false, want: "\"NOT\"", }, &litMatcher{ - pos: position{line: 90, col: 14, offset: 2432}, + pos: position{line: 104, col: 14, offset: 2573}, val: "-", ignoreCase: false, want: "\"-\"", @@ -598,12 +605,12 @@ var g = &grammar{ }, { name: "OperatorBooleanOrNode", - pos: position{line: 94, col: 1, offset: 2494}, + pos: position{line: 108, col: 1, offset: 2635}, expr: &actionExpr{ - pos: position{line: 95, col: 5, offset: 2523}, + pos: position{line: 109, col: 5, offset: 2664}, run: (*parser).callonOperatorBooleanOrNode1, expr: &litMatcher{ - pos: position{line: 95, col: 6, offset: 2524}, + pos: position{line: 109, col: 6, offset: 2665}, val: "OR", ignoreCase: false, want: "\"OR\"", @@ -612,12 +619,12 @@ var g = &grammar{ }, { name: "OperatorColonNode", - pos: position{line: 99, col: 1, offset: 2587}, + pos: position{line: 113, col: 1, offset: 2728}, expr: &actionExpr{ - pos: position{line: 100, col: 5, offset: 2612}, + pos: position{line: 114, col: 5, offset: 2753}, run: (*parser).callonOperatorColonNode1, expr: &litMatcher{ - pos: position{line: 100, col: 5, offset: 2612}, + pos: position{line: 114, col: 5, offset: 2753}, val: ":", ignoreCase: false, want: "\":\"", @@ -626,12 +633,12 @@ var g = &grammar{ }, { name: "OperatorEqualNode", - pos: position{line: 104, col: 1, offset: 2673}, + pos: position{line: 118, col: 1, offset: 2814}, expr: &actionExpr{ - pos: position{line: 105, col: 5, offset: 2698}, + pos: position{line: 119, col: 5, offset: 2839}, run: (*parser).callonOperatorEqualNode1, expr: &litMatcher{ - pos: position{line: 105, col: 5, offset: 2698}, + pos: position{line: 119, col: 5, offset: 2839}, val: "=", ignoreCase: false, want: "\"=\"", @@ -640,12 +647,12 @@ var g = &grammar{ }, { name: "OperatorLessNode", - pos: position{line: 109, col: 1, offset: 2759}, + pos: position{line: 123, col: 1, offset: 2900}, expr: &actionExpr{ - pos: position{line: 110, col: 5, offset: 2783}, + pos: position{line: 124, col: 5, offset: 2924}, run: (*parser).callonOperatorLessNode1, expr: &litMatcher{ - pos: position{line: 110, col: 5, offset: 2783}, + pos: position{line: 124, col: 5, offset: 2924}, val: "<", ignoreCase: false, want: "\"<\"", @@ -654,12 +661,12 @@ var g = &grammar{ }, { name: "OperatorLessOrEqualNode", - pos: position{line: 114, col: 1, offset: 2844}, + pos: position{line: 128, col: 1, offset: 2985}, expr: &actionExpr{ - pos: position{line: 115, col: 5, offset: 2875}, + pos: position{line: 129, col: 5, offset: 3016}, run: (*parser).callonOperatorLessOrEqualNode1, expr: &litMatcher{ - pos: position{line: 115, col: 5, offset: 2875}, + pos: position{line: 129, col: 5, offset: 3016}, val: "<=", ignoreCase: false, want: "\"<=\"", @@ -668,12 +675,12 @@ var g = &grammar{ }, { name: "OperatorGreaterNode", - pos: position{line: 119, col: 1, offset: 2937}, + pos: position{line: 133, col: 1, offset: 3078}, expr: &actionExpr{ - pos: position{line: 120, col: 5, offset: 2964}, + pos: position{line: 134, col: 5, offset: 3105}, run: (*parser).callonOperatorGreaterNode1, expr: &litMatcher{ - pos: position{line: 120, col: 5, offset: 2964}, + pos: position{line: 134, col: 5, offset: 3105}, val: ">", ignoreCase: false, want: "\">\"", @@ -682,12 +689,12 @@ var g = &grammar{ }, { name: "OperatorGreaterOrEqualNode", - pos: position{line: 124, col: 1, offset: 3025}, + pos: position{line: 138, col: 1, offset: 3166}, expr: &actionExpr{ - pos: position{line: 125, col: 5, offset: 3059}, + pos: position{line: 139, col: 5, offset: 3200}, run: (*parser).callonOperatorGreaterOrEqualNode1, expr: &litMatcher{ - pos: position{line: 125, col: 5, offset: 3059}, + pos: position{line: 139, col: 5, offset: 3200}, val: ">=", ignoreCase: false, want: "\">=\"", @@ -696,27 +703,27 @@ var g = &grammar{ }, { name: "TimeYear", - pos: position{line: 134, col: 1, offset: 3245}, + pos: position{line: 148, col: 1, offset: 3386}, expr: &actionExpr{ - pos: position{line: 135, col: 5, offset: 3261}, + pos: position{line: 149, col: 5, offset: 3402}, run: (*parser).callonTimeYear1, expr: &seqExpr{ - pos: position{line: 135, col: 5, offset: 3261}, + pos: position{line: 149, col: 5, offset: 3402}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 135, col: 5, offset: 3261}, + pos: position{line: 149, col: 5, offset: 3402}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 135, col: 11, offset: 3267}, + pos: position{line: 149, col: 11, offset: 3408}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 135, col: 17, offset: 3273}, + pos: position{line: 149, col: 17, offset: 3414}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 135, col: 23, offset: 3279}, + pos: position{line: 149, col: 23, offset: 3420}, name: "Digit", }, }, @@ -725,19 +732,19 @@ var g = &grammar{ }, { name: "TimeMonth", - pos: position{line: 139, col: 1, offset: 3321}, + pos: position{line: 153, col: 1, offset: 3462}, expr: &actionExpr{ - pos: position{line: 140, col: 5, offset: 3338}, + pos: position{line: 154, col: 5, offset: 3479}, run: (*parser).callonTimeMonth1, expr: &seqExpr{ - pos: position{line: 140, col: 5, offset: 3338}, + pos: position{line: 154, col: 5, offset: 3479}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 140, col: 5, offset: 3338}, + pos: position{line: 154, col: 5, offset: 3479}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 140, col: 11, offset: 3344}, + pos: position{line: 154, col: 11, offset: 3485}, name: "Digit", }, }, @@ -746,19 +753,19 @@ var g = &grammar{ }, { name: "TimeDay", - pos: position{line: 144, col: 1, offset: 3386}, + pos: position{line: 158, col: 1, offset: 3527}, expr: &actionExpr{ - pos: position{line: 145, col: 5, offset: 3401}, + pos: position{line: 159, col: 5, offset: 3542}, run: (*parser).callonTimeDay1, expr: &seqExpr{ - pos: position{line: 145, col: 5, offset: 3401}, + pos: position{line: 159, col: 5, offset: 3542}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 145, col: 5, offset: 3401}, + pos: position{line: 159, col: 5, offset: 3542}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 145, col: 11, offset: 3407}, + pos: position{line: 159, col: 11, offset: 3548}, name: "Digit", }, }, @@ -767,19 +774,19 @@ var g = &grammar{ }, { name: "TimeHour", - pos: position{line: 149, col: 1, offset: 3449}, + pos: position{line: 163, col: 1, offset: 3590}, expr: &actionExpr{ - pos: position{line: 150, col: 5, offset: 3465}, + pos: position{line: 164, col: 5, offset: 3606}, run: (*parser).callonTimeHour1, expr: &seqExpr{ - pos: position{line: 150, col: 5, offset: 3465}, + pos: position{line: 164, col: 5, offset: 3606}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 150, col: 5, offset: 3465}, + pos: position{line: 164, col: 5, offset: 3606}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 150, col: 11, offset: 3471}, + pos: position{line: 164, col: 11, offset: 3612}, name: "Digit", }, }, @@ -788,19 +795,19 @@ var g = &grammar{ }, { name: "TimeMinute", - pos: position{line: 154, col: 1, offset: 3513}, + pos: position{line: 168, col: 1, offset: 3654}, expr: &actionExpr{ - pos: position{line: 155, col: 5, offset: 3531}, + pos: position{line: 169, col: 5, offset: 3672}, run: (*parser).callonTimeMinute1, expr: &seqExpr{ - pos: position{line: 155, col: 5, offset: 3531}, + pos: position{line: 169, col: 5, offset: 3672}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 155, col: 5, offset: 3531}, + pos: position{line: 169, col: 5, offset: 3672}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 155, col: 11, offset: 3537}, + pos: position{line: 169, col: 11, offset: 3678}, name: "Digit", }, }, @@ -809,19 +816,19 @@ var g = &grammar{ }, { name: "TimeSecond", - pos: position{line: 159, col: 1, offset: 3579}, + pos: position{line: 173, col: 1, offset: 3720}, expr: &actionExpr{ - pos: position{line: 160, col: 5, offset: 3597}, + pos: position{line: 174, col: 5, offset: 3738}, run: (*parser).callonTimeSecond1, expr: &seqExpr{ - pos: position{line: 160, col: 5, offset: 3597}, + pos: position{line: 174, col: 5, offset: 3738}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 160, col: 5, offset: 3597}, + pos: position{line: 174, col: 5, offset: 3738}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 160, col: 11, offset: 3603}, + pos: position{line: 174, col: 11, offset: 3744}, name: "Digit", }, }, @@ -830,35 +837,35 @@ var g = &grammar{ }, { name: "FullDate", - pos: position{line: 164, col: 1, offset: 3645}, + pos: position{line: 178, col: 1, offset: 3786}, expr: &actionExpr{ - pos: position{line: 165, col: 5, offset: 3661}, + pos: position{line: 179, col: 5, offset: 3802}, run: (*parser).callonFullDate1, expr: &seqExpr{ - pos: position{line: 165, col: 5, offset: 3661}, + pos: position{line: 179, col: 5, offset: 3802}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 165, col: 5, offset: 3661}, + pos: position{line: 179, col: 5, offset: 3802}, name: "TimeYear", }, &litMatcher{ - pos: position{line: 165, col: 14, offset: 3670}, + pos: position{line: 179, col: 14, offset: 3811}, val: "-", ignoreCase: false, want: "\"-\"", }, &ruleRefExpr{ - pos: position{line: 165, col: 18, offset: 3674}, + pos: position{line: 179, col: 18, offset: 3815}, name: "TimeMonth", }, &litMatcher{ - pos: position{line: 165, col: 28, offset: 3684}, + pos: position{line: 179, col: 28, offset: 3825}, val: "-", ignoreCase: false, want: "\"-\"", }, &ruleRefExpr{ - pos: position{line: 165, col: 32, offset: 3688}, + pos: position{line: 179, col: 32, offset: 3829}, name: "TimeDay", }, }, @@ -867,52 +874,52 @@ var g = &grammar{ }, { name: "FullTime", - pos: position{line: 169, col: 1, offset: 3732}, + pos: position{line: 183, col: 1, offset: 3873}, expr: &actionExpr{ - pos: position{line: 170, col: 5, offset: 3748}, + pos: position{line: 184, col: 5, offset: 3889}, run: (*parser).callonFullTime1, expr: &seqExpr{ - pos: position{line: 170, col: 5, offset: 3748}, + pos: position{line: 184, col: 5, offset: 3889}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 170, col: 5, offset: 3748}, + pos: position{line: 184, col: 5, offset: 3889}, name: "TimeHour", }, &litMatcher{ - pos: position{line: 170, col: 14, offset: 3757}, + pos: position{line: 184, col: 14, offset: 3898}, val: ":", ignoreCase: false, want: "\":\"", }, &ruleRefExpr{ - pos: position{line: 170, col: 18, offset: 3761}, + pos: position{line: 184, col: 18, offset: 3902}, name: "TimeMinute", }, &litMatcher{ - pos: position{line: 170, col: 29, offset: 3772}, + pos: position{line: 184, col: 29, offset: 3913}, val: ":", ignoreCase: false, want: "\":\"", }, &ruleRefExpr{ - pos: position{line: 170, col: 33, offset: 3776}, + pos: position{line: 184, col: 33, offset: 3917}, name: "TimeSecond", }, &zeroOrOneExpr{ - pos: position{line: 170, col: 44, offset: 3787}, + pos: position{line: 184, col: 44, offset: 3928}, expr: &seqExpr{ - pos: position{line: 170, col: 45, offset: 3788}, + pos: position{line: 184, col: 45, offset: 3929}, exprs: []any{ &litMatcher{ - pos: position{line: 170, col: 45, offset: 3788}, + pos: position{line: 184, col: 45, offset: 3929}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 170, col: 49, offset: 3792}, + pos: position{line: 184, col: 49, offset: 3933}, expr: &ruleRefExpr{ - pos: position{line: 170, col: 49, offset: 3792}, + pos: position{line: 184, col: 49, offset: 3933}, name: "Digit", }, }, @@ -920,28 +927,28 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 170, col: 59, offset: 3802}, + pos: position{line: 184, col: 59, offset: 3943}, alternatives: []any{ &litMatcher{ - pos: position{line: 170, col: 59, offset: 3802}, + pos: position{line: 184, col: 59, offset: 3943}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 170, col: 65, offset: 3808}, + pos: position{line: 184, col: 65, offset: 3949}, exprs: []any{ &choiceExpr{ - pos: position{line: 170, col: 66, offset: 3809}, + pos: position{line: 184, col: 66, offset: 3950}, alternatives: []any{ &litMatcher{ - pos: position{line: 170, col: 66, offset: 3809}, + pos: position{line: 184, col: 66, offset: 3950}, val: "+", ignoreCase: false, want: "\"+\"", }, &litMatcher{ - pos: position{line: 170, col: 72, offset: 3815}, + pos: position{line: 184, col: 72, offset: 3956}, val: "-", ignoreCase: false, want: "\"-\"", @@ -949,17 +956,17 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 170, col: 77, offset: 3820}, + pos: position{line: 184, col: 77, offset: 3961}, name: "TimeHour", }, &litMatcher{ - pos: position{line: 170, col: 86, offset: 3829}, + pos: position{line: 184, col: 86, offset: 3970}, val: ":", ignoreCase: false, want: "\":\"", }, &ruleRefExpr{ - pos: position{line: 170, col: 90, offset: 3833}, + pos: position{line: 184, col: 90, offset: 3974}, name: "TimeMinute", }, }, @@ -970,14 +977,41 @@ var g = &grammar{ }, }, }, + { + name: "DateTime", + pos: position{line: 188, col: 1, offset: 4022}, + expr: &actionExpr{ + pos: position{line: 189, col: 5, offset: 4035}, + run: (*parser).callonDateTime1, + expr: &seqExpr{ + pos: position{line: 189, col: 5, offset: 4035}, + exprs: []any{ + &ruleRefExpr{ + pos: position{line: 189, col: 5, offset: 4035}, + name: "FullDate", + }, + &litMatcher{ + pos: position{line: 189, col: 14, offset: 4044}, + val: "T", + ignoreCase: false, + want: "\"T\"", + }, + &ruleRefExpr{ + pos: position{line: 189, col: 18, offset: 4048}, + name: "FullTime", + }, + }, + }, + }, + }, { name: "Char", - pos: position{line: 178, col: 1, offset: 4004}, + pos: position{line: 197, col: 1, offset: 4214}, expr: &actionExpr{ - pos: position{line: 179, col: 5, offset: 4016}, + pos: position{line: 198, col: 5, offset: 4226}, run: (*parser).callonChar1, expr: &charClassMatcher{ - pos: position{line: 179, col: 5, offset: 4016}, + pos: position{line: 198, col: 5, offset: 4226}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -987,26 +1021,26 @@ var g = &grammar{ }, { name: "String", - pos: position{line: 183, col: 1, offset: 4061}, + pos: position{line: 202, col: 1, offset: 4271}, expr: &actionExpr{ - pos: position{line: 184, col: 5, offset: 4075}, + pos: position{line: 203, col: 5, offset: 4285}, run: (*parser).callonString1, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 4075}, + pos: position{line: 203, col: 5, offset: 4285}, exprs: []any{ &litMatcher{ - pos: position{line: 184, col: 5, offset: 4075}, + pos: position{line: 203, col: 5, offset: 4285}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 184, col: 9, offset: 4079}, + pos: position{line: 203, col: 9, offset: 4289}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 184, col: 11, offset: 4081}, + pos: position{line: 203, col: 11, offset: 4291}, expr: &charClassMatcher{ - pos: position{line: 184, col: 11, offset: 4081}, + pos: position{line: 203, col: 11, offset: 4291}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1015,7 +1049,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 17, offset: 4087}, + pos: position{line: 203, col: 17, offset: 4297}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1026,12 +1060,12 @@ var g = &grammar{ }, { name: "Digit", - pos: position{line: 188, col: 1, offset: 4122}, + pos: position{line: 207, col: 1, offset: 4332}, expr: &actionExpr{ - pos: position{line: 189, col: 5, offset: 4135}, + pos: position{line: 208, col: 5, offset: 4345}, run: (*parser).callonDigit1, expr: &charClassMatcher{ - pos: position{line: 189, col: 5, offset: 4135}, + pos: position{line: 208, col: 5, offset: 4345}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1041,11 +1075,11 @@ var g = &grammar{ }, { name: "_", - pos: position{line: 193, col: 1, offset: 4177}, + pos: position{line: 212, col: 1, offset: 4387}, expr: &zeroOrMoreExpr{ - pos: position{line: 194, col: 5, offset: 4186}, + pos: position{line: 213, col: 5, offset: 4396}, expr: &charClassMatcher{ - pos: position{line: 194, col: 5, offset: 4186}, + pos: position{line: 213, col: 5, offset: 4396}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1331,6 +1365,17 @@ func (p *parser) callonFullTime1() (any, error) { return p.cur.onFullTime1() } +func (c *current) onDateTime1() (any, error) { + return c.text, nil + +} + +func (p *parser) callonDateTime1() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onDateTime1() +} + func (c *current) onChar1() (any, error) { return c.text, nil diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index faafbf19038..7421f524541 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/araddon/dateparse" tAssert "github.com/stretchr/testify/assert" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" @@ -14,7 +15,7 @@ import ( ) var mustParseTime = func(t *testing.T, ts string) time.Time { - tp, err := time.Parse(time.RFC3339Nano, ts) + tp, err := dateparse.ParseLocal(ts) if err != nil { t.Fatalf("time.Parse(...) error = %v", err) } @@ -62,9 +63,13 @@ func TestParse(t *testing.T) { expectedError error }{ // SPEC ////////////////////////////////////////////////////////////////////////////// + // // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf + // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 // + // ++ // 2.1.2 AND Operator + // 3.1.2 AND Operator { name: `cat AND dog`, expectedAst: &ast.Ast{ @@ -83,6 +88,51 @@ func TestParse(t *testing.T) { name: `AND cat AND dog`, expectedError: errors.New(""), }, + // ++ + // 2.1.6 NOT Operator + // 3.1.6 NOT Operator + { + name: `cat NOT dog`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, + }, + }, + }, + { + name: `NOT dog`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, + }, + }, + }, + // ++ + // 2.1.8 OR Operator + // 3.1.8 OR Operator + { + name: `cat OR dog`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "dog"}, + }, + }, + }, + { + name: `OR`, + expectedError: errors.New(""), + }, + { + name: `OR cat AND dog`, + expectedError: errors.New(""), + }, + // ++ // 3.1.11 Implicit Operator { name: `cat dog`, @@ -122,6 +172,8 @@ func TestParse(t *testing.T) { }, }, }, + // ++ + // 2.1.12 Parentheses // 3.1.12 Parentheses { name: `(cat OR dog) AND fox`, @@ -137,6 +189,7 @@ func TestParse(t *testing.T) { }, }, }, + // ++ // 3.2.3 Implicit Operator for Property Restriction { name: `author:"John Smith" filetype:docx`, @@ -198,6 +251,7 @@ func TestParse(t *testing.T) { }, }, }, + // ++ // 3.3.1.1.1 Implicit AND Operator { name: `cat +dog`, @@ -361,6 +415,37 @@ func TestParse(t *testing.T) { }, }, }, + // ++ + // 2.3.5 Date Tokens + // 3.3.5 Date Tokens + { + name: `Modified:2023-09-05`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05"), + }, + }, + }, + }, + { + name: `Modified:"2008-01-29"`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2008-01-29"), + }, + }, + }, + }, + { + name: `Modified:today`, + skip: true, + }, ////////////////////////////////////////////////////////////////////////////////////// // everything else { diff --git a/services/search/pkg/query/kql/doc.go b/services/search/pkg/query/kql/doc.go index a887affc32f..577ada26b72 100644 --- a/services/search/pkg/query/kql/doc.go +++ b/services/search/pkg/query/kql/doc.go @@ -21,7 +21,6 @@ The following spec parts are supported and tested: - 3.3.5 Date Tokens References: - - https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference - https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 - https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf */ From 5f6d45beba9fd445e73529022830a7d5a295f623 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Sat, 9 Sep 2023 13:07:12 +0200 Subject: [PATCH 04/11] enhancement: add the ability to decide how kql nodes get connected connecting nodes (with edges) seem straight forward when not using group, the default connection for nodes with the same node is always OR. THis only applies for first level nodes, for grouped nodes it is defined differently. The KQL docs are saying, nodes inside a grouped node, with the same key are connected by a AND edge. --- services/search/pkg/query/kql/cast.go | 18 +- services/search/pkg/query/kql/connect.go | 73 ++- services/search/pkg/query/kql/dictionary.peg | 27 +- .../search/pkg/query/kql/dictionary_gen.go | 570 +++++++++--------- .../search/pkg/query/kql/dictionary_test.go | 8 +- services/search/pkg/query/kql/factory.go | 29 +- services/search/pkg/query/kql/kql.go | 111 ---- 7 files changed, 367 insertions(+), 469 deletions(-) diff --git a/services/search/pkg/query/kql/cast.go b/services/search/pkg/query/kql/cast.go index 2f7c58c3531..d9710d35ce1 100644 --- a/services/search/pkg/query/kql/cast.go +++ b/services/search/pkg/query/kql/cast.go @@ -23,8 +23,24 @@ func toNodes[T ast.Node](in interface{}) ([]T, error) { switch v := in.(type) { case []T: return v, nil + case T: + return []T{v}, nil + case []interface{}: + var ts []T + for _, inter := range v { + n, err := toNodes[T](inter) + if err != nil { + return nil, err + } + + ts = append(ts, n...) + } + return ts, nil + case nil: + return nil, nil default: - return nil, fmt.Errorf("can't convert '%T' to []ast.Node", in) + var t T + return nil, fmt.Errorf("can't convert '%T' to '%T'", in, t) } } diff --git a/services/search/pkg/query/kql/connect.go b/services/search/pkg/query/kql/connect.go index 3c926d688ce..4f7dce66672 100644 --- a/services/search/pkg/query/kql/connect.go +++ b/services/search/pkg/query/kql/connect.go @@ -13,12 +13,13 @@ func connectNodes(c Connector, nodes ...ast.Node) []ast.Node { for i := range nodes { ri := len(nodes) - 1 - i head := nodes[ri] + pair := []ast.Node{head} - if connectionNodes := connectNode(c, head, connectedNodes...); len(connectionNodes) > 0 { - connectedNodes = append(connectionNodes, connectedNodes...) + if connectionNodes := connectNode(c, pair[0], connectedNodes...); len(connectionNodes) >= 1 { + pair = append(pair, connectionNodes...) } - connectedNodes = append([]ast.Node{head}, connectedNodes...) + connectedNodes = append(pair, connectedNodes...) } return connectedNodes @@ -65,8 +66,8 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections return nil } - headKey := strings.ToLower(ast.NodeKey(head)) - neighborKey := strings.ToLower(ast.NodeKey(neighbor)) + headKey := strings.ToLower(c.nodeKey(head)) + neighborKey := strings.ToLower(c.nodeKey(neighbor)) connection := &ast.OperatorNode{ Base: &ast.Base{Loc: &ast.Location{Source: &[]string{"implicitly operator"}[0]}}, @@ -74,34 +75,47 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections } // if the current node and the neighbor node have the same key - // the connection is of type OR + // the connection is of type OR, same applies if no keys are in place + // + // "" == "" // // spec: same // author:"John Smith" author:"Jane Smith" // author:"John Smith" OR author:"Jane Smith" // - // if the nodes have NO key, the edge is a AND connection - // - // spec: same - // cat dog - // cat AND dog - // from the spec: - // To construct complex queries, you can combine multiple - // free-text expressions with KQL query operators. - // If there are multiple free-text expressions without any - // operators in between them, the query behavior is the same - // as using the AND operator. - // - // nodes inside of group node are handled differently, - // if no explicit operator given, it uses AND + // nodes inside of group nodes are handled differently, + // if no explicit operator give, it uses OR // // spec: same // author:"John Smith" AND author:"Jane Smith" // author:("John Smith" "Jane Smith") - if headKey == neighborKey && headKey != "" && neighborKey != "" { + if headKey == neighborKey { connection.Value = c.sameKeyOPValue } + // decisions based on nearest neighbor node + switch neighbor.(type) { + // nearest neighbor node type could change the default case + // docs says, if the next value node: + // + // is a group AND has no key + // + // even if the current node has none too, which normal leads to SAME KEY OR + // + // it should be an AND edge + // + // spec: same + // cat (dog OR fox) + // cat AND (dog OR fox) + // + // note: + // sounds contradictory to me + case *ast.GroupNode: + if headKey == "" && neighborKey == "" { + connection.Value = BoolAND + } + } + // decisions based on nearest neighbor operators for i, node := range connections { // consider direct neighbor operator only @@ -114,7 +128,7 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections } } - // if neighbor node negotiates, an AND edge is needed + // if neighbor node negotiates, AND edge is needed // // spec: same // cat -dog @@ -127,3 +141,18 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections return []ast.Node{connection} } + +func (c DefaultConnector) nodeKey(n ast.Node) string { + switch node := n.(type) { + case *ast.StringNode: + return node.Key + case *ast.DateTimeNode: + return node.Key + case *ast.BooleanNode: + return node.Key + case *ast.GroupNode: + return node.Key + default: + return "" + } +} diff --git a/services/search/pkg/query/kql/dictionary.peg b/services/search/pkg/query/kql/dictionary.peg index df8784d807c..999d25bb90b 100644 --- a/services/search/pkg/query/kql/dictionary.peg +++ b/services/search/pkg/query/kql/dictionary.peg @@ -10,19 +10,22 @@ AST <- _ !( OperatorBooleanAndNode / OperatorBooleanOrNode - ) _ nodes:Nodes _ { - return buildAST(nodes, c.text, c.pos) + ) n:Nodes { + return buildAST(n, c.text, c.pos) } +//////////////////////////////////////////////////////// +// nodes +//////////////////////////////////////////////////////// + Nodes <- - _ head:( - GroupNode / - PropertyRestrictionNodes / - OperatorBooleanNodes / - FreeTextKeywordNodes - ) _ tail:Nodes? { - return buildNodes(head, tail) - } + (_ Node)+ + +Node <- + GroupNode / + PropertyRestrictionNodes / + OperatorBooleanNodes / + FreeTextKeywordNodes //////////////////////////////////////////////////////// // nesting @@ -210,4 +213,6 @@ Digit <- } _ <- - [ \t]* + [ \t]* { + return nil, nil + } diff --git a/services/search/pkg/query/kql/dictionary_gen.go b/services/search/pkg/query/kql/dictionary_gen.go index 8f83d73d105..ba3f637b3f1 100644 --- a/services/search/pkg/query/kql/dictionary_gen.go +++ b/services/search/pkg/query/kql/dictionary_gen.go @@ -48,138 +48,118 @@ var g = &grammar{ }, }, }, - &ruleRefExpr{ - pos: position{line: 13, col: 7, offset: 228}, - name: "_", - }, &labeledExpr{ - pos: position{line: 13, col: 9, offset: 230}, - label: "nodes", + pos: position{line: 13, col: 7, offset: 228}, + label: "n", expr: &ruleRefExpr{ - pos: position{line: 13, col: 15, offset: 236}, + pos: position{line: 13, col: 9, offset: 230}, name: "Nodes", }, }, - &ruleRefExpr{ - pos: position{line: 13, col: 21, offset: 242}, - name: "_", - }, }, }, }, }, { name: "Nodes", - pos: position{line: 17, col: 1, offset: 299}, - expr: &actionExpr{ - pos: position{line: 18, col: 5, offset: 312}, - run: (*parser).callonNodes1, + pos: position{line: 21, col: 1, offset: 411}, + expr: &oneOrMoreExpr{ + pos: position{line: 22, col: 5, offset: 424}, expr: &seqExpr{ - pos: position{line: 18, col: 5, offset: 312}, + pos: position{line: 22, col: 6, offset: 425}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 18, col: 5, offset: 312}, + pos: position{line: 22, col: 6, offset: 425}, name: "_", }, - &labeledExpr{ - pos: position{line: 18, col: 7, offset: 314}, - label: "head", - expr: &choiceExpr{ - pos: position{line: 19, col: 9, offset: 329}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 19, col: 9, offset: 329}, - name: "GroupNode", - }, - &ruleRefExpr{ - pos: position{line: 20, col: 9, offset: 349}, - name: "PropertyRestrictionNodes", - }, - &ruleRefExpr{ - pos: position{line: 21, col: 9, offset: 384}, - name: "OperatorBooleanNodes", - }, - &ruleRefExpr{ - pos: position{line: 22, col: 9, offset: 415}, - name: "FreeTextKeywordNodes", - }, - }, - }, - }, &ruleRefExpr{ - pos: position{line: 23, col: 7, offset: 442}, - name: "_", - }, - &labeledExpr{ - pos: position{line: 23, col: 9, offset: 444}, - label: "tail", - expr: &zeroOrOneExpr{ - pos: position{line: 23, col: 14, offset: 449}, - expr: &ruleRefExpr{ - pos: position{line: 23, col: 14, offset: 449}, - name: "Nodes", - }, - }, + pos: position{line: 22, col: 8, offset: 427}, + name: "Node", }, }, }, }, }, + { + name: "Node", + pos: position{line: 24, col: 1, offset: 435}, + expr: &choiceExpr{ + pos: position{line: 25, col: 5, offset: 447}, + alternatives: []any{ + &ruleRefExpr{ + pos: position{line: 25, col: 5, offset: 447}, + name: "GroupNode", + }, + &ruleRefExpr{ + pos: position{line: 26, col: 5, offset: 463}, + name: "PropertyRestrictionNodes", + }, + &ruleRefExpr{ + pos: position{line: 27, col: 5, offset: 494}, + name: "OperatorBooleanNodes", + }, + &ruleRefExpr{ + pos: position{line: 28, col: 5, offset: 521}, + name: "FreeTextKeywordNodes", + }, + }, + }, + }, { name: "GroupNode", - pos: position{line: 31, col: 1, offset: 627}, + pos: position{line: 34, col: 1, offset: 669}, expr: &actionExpr{ - pos: position{line: 32, col: 5, offset: 644}, + pos: position{line: 35, col: 5, offset: 686}, run: (*parser).callonGroupNode1, expr: &seqExpr{ - pos: position{line: 32, col: 5, offset: 644}, + pos: position{line: 35, col: 5, offset: 686}, exprs: []any{ &labeledExpr{ - pos: position{line: 32, col: 5, offset: 644}, + pos: position{line: 35, col: 5, offset: 686}, label: "k", expr: &zeroOrOneExpr{ - pos: position{line: 32, col: 7, offset: 646}, + pos: position{line: 35, col: 7, offset: 688}, expr: &oneOrMoreExpr{ - pos: position{line: 32, col: 8, offset: 647}, + pos: position{line: 35, col: 8, offset: 689}, expr: &ruleRefExpr{ - pos: position{line: 32, col: 8, offset: 647}, + pos: position{line: 35, col: 8, offset: 689}, name: "Char", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 32, col: 16, offset: 655}, + pos: position{line: 35, col: 16, offset: 697}, expr: &choiceExpr{ - pos: position{line: 32, col: 17, offset: 656}, + pos: position{line: 35, col: 17, offset: 698}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 32, col: 17, offset: 656}, + pos: position{line: 35, col: 17, offset: 698}, name: "OperatorColonNode", }, &ruleRefExpr{ - pos: position{line: 32, col: 37, offset: 676}, + pos: position{line: 35, col: 37, offset: 718}, name: "OperatorEqualNode", }, }, }, }, &litMatcher{ - pos: position{line: 32, col: 57, offset: 696}, + pos: position{line: 35, col: 57, offset: 738}, val: "(", ignoreCase: false, want: "\"(\"", }, &labeledExpr{ - pos: position{line: 32, col: 61, offset: 700}, + pos: position{line: 35, col: 61, offset: 742}, label: "v", expr: &ruleRefExpr{ - pos: position{line: 32, col: 63, offset: 702}, + pos: position{line: 35, col: 63, offset: 744}, name: "Nodes", }, }, &litMatcher{ - pos: position{line: 32, col: 69, offset: 708}, + pos: position{line: 35, col: 69, offset: 750}, val: ")", ignoreCase: false, want: "\")\"", @@ -190,20 +170,20 @@ var g = &grammar{ }, { name: "PropertyRestrictionNodes", - pos: position{line: 40, col: 1, offset: 912}, + pos: position{line: 43, col: 1, offset: 954}, expr: &choiceExpr{ - pos: position{line: 41, col: 5, offset: 944}, + pos: position{line: 44, col: 5, offset: 986}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 41, col: 5, offset: 944}, + pos: position{line: 44, col: 5, offset: 986}, name: "YesNoPropertyRestrictionNode", }, &ruleRefExpr{ - pos: position{line: 42, col: 5, offset: 979}, + pos: position{line: 45, col: 5, offset: 1021}, name: "DateTimeRestrictionNode", }, &ruleRefExpr{ - pos: position{line: 43, col: 5, offset: 1009}, + pos: position{line: 46, col: 5, offset: 1051}, name: "TextPropertyRestrictionNode", }, }, @@ -211,51 +191,51 @@ var g = &grammar{ }, { name: "YesNoPropertyRestrictionNode", - pos: position{line: 45, col: 1, offset: 1038}, + pos: position{line: 48, col: 1, offset: 1080}, expr: &actionExpr{ - pos: position{line: 46, col: 5, offset: 1074}, + pos: position{line: 49, col: 5, offset: 1116}, run: (*parser).callonYesNoPropertyRestrictionNode1, expr: &seqExpr{ - pos: position{line: 46, col: 5, offset: 1074}, + pos: position{line: 49, col: 5, offset: 1116}, exprs: []any{ &labeledExpr{ - pos: position{line: 46, col: 5, offset: 1074}, + pos: position{line: 49, col: 5, offset: 1116}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 46, col: 7, offset: 1076}, + pos: position{line: 49, col: 7, offset: 1118}, expr: &ruleRefExpr{ - pos: position{line: 46, col: 7, offset: 1076}, + pos: position{line: 49, col: 7, offset: 1118}, name: "Char", }, }, }, &choiceExpr{ - pos: position{line: 46, col: 14, offset: 1083}, + pos: position{line: 49, col: 14, offset: 1125}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 46, col: 14, offset: 1083}, + pos: position{line: 49, col: 14, offset: 1125}, name: "OperatorColonNode", }, &ruleRefExpr{ - pos: position{line: 46, col: 34, offset: 1103}, + pos: position{line: 49, col: 34, offset: 1145}, name: "OperatorEqualNode", }, }, }, &labeledExpr{ - pos: position{line: 46, col: 53, offset: 1122}, + pos: position{line: 49, col: 53, offset: 1164}, label: "v", expr: &choiceExpr{ - pos: position{line: 46, col: 56, offset: 1125}, + pos: position{line: 49, col: 56, offset: 1167}, alternatives: []any{ &litMatcher{ - pos: position{line: 46, col: 56, offset: 1125}, + pos: position{line: 49, col: 56, offset: 1167}, val: "true", ignoreCase: false, want: "\"true\"", }, &litMatcher{ - pos: position{line: 46, col: 65, offset: 1134}, + pos: position{line: 49, col: 65, offset: 1176}, val: "false", ignoreCase: false, want: "\"false\"", @@ -269,91 +249,91 @@ var g = &grammar{ }, { name: "DateTimeRestrictionNode", - pos: position{line: 50, col: 1, offset: 1204}, + pos: position{line: 53, col: 1, offset: 1246}, expr: &actionExpr{ - pos: position{line: 51, col: 5, offset: 1235}, + pos: position{line: 54, col: 5, offset: 1277}, run: (*parser).callonDateTimeRestrictionNode1, expr: &seqExpr{ - pos: position{line: 51, col: 5, offset: 1235}, + pos: position{line: 54, col: 5, offset: 1277}, exprs: []any{ &labeledExpr{ - pos: position{line: 51, col: 5, offset: 1235}, + pos: position{line: 54, col: 5, offset: 1277}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 51, col: 7, offset: 1237}, + pos: position{line: 54, col: 7, offset: 1279}, expr: &ruleRefExpr{ - pos: position{line: 51, col: 7, offset: 1237}, + pos: position{line: 54, col: 7, offset: 1279}, name: "Char", }, }, }, &labeledExpr{ - pos: position{line: 51, col: 13, offset: 1243}, + pos: position{line: 54, col: 13, offset: 1285}, label: "o", expr: &choiceExpr{ - pos: position{line: 52, col: 9, offset: 1255}, + pos: position{line: 55, col: 9, offset: 1297}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 52, col: 9, offset: 1255}, + pos: position{line: 55, col: 9, offset: 1297}, name: "OperatorGreaterOrEqualNode", }, &ruleRefExpr{ - pos: position{line: 53, col: 9, offset: 1292}, + pos: position{line: 56, col: 9, offset: 1334}, name: "OperatorLessOrEqualNode", }, &ruleRefExpr{ - pos: position{line: 54, col: 9, offset: 1326}, + pos: position{line: 57, col: 9, offset: 1368}, name: "OperatorGreaterNode", }, &ruleRefExpr{ - pos: position{line: 55, col: 9, offset: 1356}, + pos: position{line: 58, col: 9, offset: 1398}, name: "OperatorLessNode", }, &ruleRefExpr{ - pos: position{line: 56, col: 9, offset: 1383}, + pos: position{line: 59, col: 9, offset: 1425}, name: "OperatorEqualNode", }, &ruleRefExpr{ - pos: position{line: 57, col: 9, offset: 1411}, + pos: position{line: 60, col: 9, offset: 1453}, name: "OperatorColonNode", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 58, col: 7, offset: 1435}, + pos: position{line: 61, col: 7, offset: 1477}, expr: &litMatcher{ - pos: position{line: 58, col: 7, offset: 1435}, + pos: position{line: 61, col: 7, offset: 1477}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, }, &labeledExpr{ - pos: position{line: 58, col: 12, offset: 1440}, + pos: position{line: 61, col: 12, offset: 1482}, label: "v", expr: &choiceExpr{ - pos: position{line: 59, col: 9, offset: 1452}, + pos: position{line: 62, col: 9, offset: 1494}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 59, col: 9, offset: 1452}, + pos: position{line: 62, col: 9, offset: 1494}, name: "DateTime", }, &ruleRefExpr{ - pos: position{line: 60, col: 9, offset: 1471}, + pos: position{line: 63, col: 9, offset: 1513}, name: "FullDate", }, &ruleRefExpr{ - pos: position{line: 61, col: 9, offset: 1490}, + pos: position{line: 64, col: 9, offset: 1532}, name: "FullTime", }, }, }, }, &zeroOrOneExpr{ - pos: position{line: 62, col: 7, offset: 1505}, + pos: position{line: 65, col: 7, offset: 1547}, expr: &litMatcher{ - pos: position{line: 62, col: 7, offset: 1505}, + pos: position{line: 65, col: 7, offset: 1547}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -365,51 +345,51 @@ var g = &grammar{ }, { name: "TextPropertyRestrictionNode", - pos: position{line: 66, col: 1, offset: 1576}, + pos: position{line: 69, col: 1, offset: 1618}, expr: &actionExpr{ - pos: position{line: 67, col: 5, offset: 1611}, + pos: position{line: 70, col: 5, offset: 1653}, run: (*parser).callonTextPropertyRestrictionNode1, expr: &seqExpr{ - pos: position{line: 67, col: 5, offset: 1611}, + pos: position{line: 70, col: 5, offset: 1653}, exprs: []any{ &labeledExpr{ - pos: position{line: 67, col: 5, offset: 1611}, + pos: position{line: 70, col: 5, offset: 1653}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 67, col: 7, offset: 1613}, + pos: position{line: 70, col: 7, offset: 1655}, expr: &ruleRefExpr{ - pos: position{line: 67, col: 7, offset: 1613}, + pos: position{line: 70, col: 7, offset: 1655}, name: "Char", }, }, }, &choiceExpr{ - pos: position{line: 67, col: 14, offset: 1620}, + pos: position{line: 70, col: 14, offset: 1662}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 67, col: 14, offset: 1620}, + pos: position{line: 70, col: 14, offset: 1662}, name: "OperatorColonNode", }, &ruleRefExpr{ - pos: position{line: 67, col: 34, offset: 1640}, + pos: position{line: 70, col: 34, offset: 1682}, name: "OperatorEqualNode", }, }, }, &labeledExpr{ - pos: position{line: 67, col: 53, offset: 1659}, + pos: position{line: 70, col: 53, offset: 1701}, label: "v", expr: &choiceExpr{ - pos: position{line: 67, col: 56, offset: 1662}, + pos: position{line: 70, col: 56, offset: 1704}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 67, col: 56, offset: 1662}, + pos: position{line: 70, col: 56, offset: 1704}, name: "String", }, &oneOrMoreExpr{ - pos: position{line: 67, col: 65, offset: 1671}, + pos: position{line: 70, col: 65, offset: 1713}, expr: &charClassMatcher{ - pos: position{line: 67, col: 65, offset: 1671}, + pos: position{line: 70, col: 65, offset: 1713}, val: "[^ ()]", chars: []rune{' ', '(', ')'}, ignoreCase: false, @@ -425,16 +405,16 @@ var g = &grammar{ }, { name: "FreeTextKeywordNodes", - pos: position{line: 75, col: 1, offset: 1877}, + pos: position{line: 78, col: 1, offset: 1919}, expr: &choiceExpr{ - pos: position{line: 76, col: 5, offset: 1905}, + pos: position{line: 79, col: 5, offset: 1947}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 76, col: 5, offset: 1905}, + pos: position{line: 79, col: 5, offset: 1947}, name: "PhraseNode", }, &ruleRefExpr{ - pos: position{line: 77, col: 5, offset: 1922}, + pos: position{line: 80, col: 5, offset: 1964}, name: "WordNode", }, }, @@ -442,40 +422,40 @@ var g = &grammar{ }, { name: "PhraseNode", - pos: position{line: 79, col: 1, offset: 1932}, + pos: position{line: 82, col: 1, offset: 1974}, expr: &actionExpr{ - pos: position{line: 80, col: 6, offset: 1951}, + pos: position{line: 83, col: 6, offset: 1993}, run: (*parser).callonPhraseNode1, expr: &seqExpr{ - pos: position{line: 80, col: 6, offset: 1951}, + pos: position{line: 83, col: 6, offset: 1993}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 80, col: 6, offset: 1951}, + pos: position{line: 83, col: 6, offset: 1993}, expr: &ruleRefExpr{ - pos: position{line: 80, col: 6, offset: 1951}, + pos: position{line: 83, col: 6, offset: 1993}, name: "OperatorColonNode", }, }, &ruleRefExpr{ - pos: position{line: 80, col: 25, offset: 1970}, + pos: position{line: 83, col: 25, offset: 2012}, name: "_", }, &labeledExpr{ - pos: position{line: 80, col: 27, offset: 1972}, + pos: position{line: 83, col: 27, offset: 2014}, label: "v", expr: &ruleRefExpr{ - pos: position{line: 80, col: 29, offset: 1974}, + pos: position{line: 83, col: 29, offset: 2016}, name: "String", }, }, &ruleRefExpr{ - pos: position{line: 80, col: 36, offset: 1981}, + pos: position{line: 83, col: 36, offset: 2023}, name: "_", }, &zeroOrOneExpr{ - pos: position{line: 80, col: 38, offset: 1983}, + pos: position{line: 83, col: 38, offset: 2025}, expr: &ruleRefExpr{ - pos: position{line: 80, col: 38, offset: 1983}, + pos: position{line: 83, col: 38, offset: 2025}, name: "OperatorColonNode", }, }, @@ -485,31 +465,31 @@ var g = &grammar{ }, { name: "WordNode", - pos: position{line: 84, col: 1, offset: 2064}, + pos: position{line: 87, col: 1, offset: 2106}, expr: &actionExpr{ - pos: position{line: 85, col: 6, offset: 2081}, + pos: position{line: 88, col: 6, offset: 2123}, run: (*parser).callonWordNode1, expr: &seqExpr{ - pos: position{line: 85, col: 6, offset: 2081}, + pos: position{line: 88, col: 6, offset: 2123}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 85, col: 6, offset: 2081}, + pos: position{line: 88, col: 6, offset: 2123}, expr: &ruleRefExpr{ - pos: position{line: 85, col: 6, offset: 2081}, + pos: position{line: 88, col: 6, offset: 2123}, name: "OperatorColonNode", }, }, &ruleRefExpr{ - pos: position{line: 85, col: 25, offset: 2100}, + pos: position{line: 88, col: 25, offset: 2142}, name: "_", }, &labeledExpr{ - pos: position{line: 85, col: 27, offset: 2102}, + pos: position{line: 88, col: 27, offset: 2144}, label: "v", expr: &oneOrMoreExpr{ - pos: position{line: 85, col: 29, offset: 2104}, + pos: position{line: 88, col: 29, offset: 2146}, expr: &charClassMatcher{ - pos: position{line: 85, col: 29, offset: 2104}, + pos: position{line: 88, col: 29, offset: 2146}, val: "[^ :()]", chars: []rune{' ', ':', '(', ')'}, ignoreCase: false, @@ -518,13 +498,13 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 85, col: 38, offset: 2113}, + pos: position{line: 88, col: 38, offset: 2155}, name: "_", }, &zeroOrOneExpr{ - pos: position{line: 85, col: 40, offset: 2115}, + pos: position{line: 88, col: 40, offset: 2157}, expr: &ruleRefExpr{ - pos: position{line: 85, col: 40, offset: 2115}, + pos: position{line: 88, col: 40, offset: 2157}, name: "OperatorColonNode", }, }, @@ -534,20 +514,20 @@ var g = &grammar{ }, { name: "OperatorBooleanNodes", - pos: position{line: 93, col: 1, offset: 2324}, + pos: position{line: 96, col: 1, offset: 2366}, expr: &choiceExpr{ - pos: position{line: 94, col: 5, offset: 2352}, + pos: position{line: 97, col: 5, offset: 2394}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 94, col: 5, offset: 2352}, + pos: position{line: 97, col: 5, offset: 2394}, name: "OperatorBooleanAndNode", }, &ruleRefExpr{ - pos: position{line: 95, col: 5, offset: 2381}, + pos: position{line: 98, col: 5, offset: 2423}, name: "OperatorBooleanNotNode", }, &ruleRefExpr{ - pos: position{line: 96, col: 5, offset: 2410}, + pos: position{line: 99, col: 5, offset: 2452}, name: "OperatorBooleanOrNode", }, }, @@ -555,21 +535,21 @@ var g = &grammar{ }, { name: "OperatorBooleanAndNode", - pos: position{line: 98, col: 1, offset: 2433}, + pos: position{line: 101, col: 1, offset: 2475}, expr: &actionExpr{ - pos: position{line: 99, col: 5, offset: 2463}, + pos: position{line: 102, col: 5, offset: 2505}, run: (*parser).callonOperatorBooleanAndNode1, expr: &choiceExpr{ - pos: position{line: 99, col: 6, offset: 2464}, + pos: position{line: 102, col: 6, offset: 2506}, alternatives: []any{ &litMatcher{ - pos: position{line: 99, col: 6, offset: 2464}, + pos: position{line: 102, col: 6, offset: 2506}, val: "AND", ignoreCase: false, want: "\"AND\"", }, &litMatcher{ - pos: position{line: 99, col: 14, offset: 2472}, + pos: position{line: 102, col: 14, offset: 2514}, val: "+", ignoreCase: false, want: "\"+\"", @@ -580,21 +560,21 @@ var g = &grammar{ }, { name: "OperatorBooleanNotNode", - pos: position{line: 103, col: 1, offset: 2534}, + pos: position{line: 106, col: 1, offset: 2576}, expr: &actionExpr{ - pos: position{line: 104, col: 5, offset: 2564}, + pos: position{line: 107, col: 5, offset: 2606}, run: (*parser).callonOperatorBooleanNotNode1, expr: &choiceExpr{ - pos: position{line: 104, col: 6, offset: 2565}, + pos: position{line: 107, col: 6, offset: 2607}, alternatives: []any{ &litMatcher{ - pos: position{line: 104, col: 6, offset: 2565}, + pos: position{line: 107, col: 6, offset: 2607}, val: "NOT", ignoreCase: false, want: "\"NOT\"", }, &litMatcher{ - pos: position{line: 104, col: 14, offset: 2573}, + pos: position{line: 107, col: 14, offset: 2615}, val: "-", ignoreCase: false, want: "\"-\"", @@ -605,12 +585,12 @@ var g = &grammar{ }, { name: "OperatorBooleanOrNode", - pos: position{line: 108, col: 1, offset: 2635}, + pos: position{line: 111, col: 1, offset: 2677}, expr: &actionExpr{ - pos: position{line: 109, col: 5, offset: 2664}, + pos: position{line: 112, col: 5, offset: 2706}, run: (*parser).callonOperatorBooleanOrNode1, expr: &litMatcher{ - pos: position{line: 109, col: 6, offset: 2665}, + pos: position{line: 112, col: 6, offset: 2707}, val: "OR", ignoreCase: false, want: "\"OR\"", @@ -619,12 +599,12 @@ var g = &grammar{ }, { name: "OperatorColonNode", - pos: position{line: 113, col: 1, offset: 2728}, + pos: position{line: 116, col: 1, offset: 2770}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2753}, + pos: position{line: 117, col: 5, offset: 2795}, run: (*parser).callonOperatorColonNode1, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2753}, + pos: position{line: 117, col: 5, offset: 2795}, val: ":", ignoreCase: false, want: "\":\"", @@ -633,12 +613,12 @@ var g = &grammar{ }, { name: "OperatorEqualNode", - pos: position{line: 118, col: 1, offset: 2814}, + pos: position{line: 121, col: 1, offset: 2856}, expr: &actionExpr{ - pos: position{line: 119, col: 5, offset: 2839}, + pos: position{line: 122, col: 5, offset: 2881}, run: (*parser).callonOperatorEqualNode1, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2839}, + pos: position{line: 122, col: 5, offset: 2881}, val: "=", ignoreCase: false, want: "\"=\"", @@ -647,12 +627,12 @@ var g = &grammar{ }, { name: "OperatorLessNode", - pos: position{line: 123, col: 1, offset: 2900}, + pos: position{line: 126, col: 1, offset: 2942}, expr: &actionExpr{ - pos: position{line: 124, col: 5, offset: 2924}, + pos: position{line: 127, col: 5, offset: 2966}, run: (*parser).callonOperatorLessNode1, expr: &litMatcher{ - pos: position{line: 124, col: 5, offset: 2924}, + pos: position{line: 127, col: 5, offset: 2966}, val: "<", ignoreCase: false, want: "\"<\"", @@ -661,12 +641,12 @@ var g = &grammar{ }, { name: "OperatorLessOrEqualNode", - pos: position{line: 128, col: 1, offset: 2985}, + pos: position{line: 131, col: 1, offset: 3027}, expr: &actionExpr{ - pos: position{line: 129, col: 5, offset: 3016}, + pos: position{line: 132, col: 5, offset: 3058}, run: (*parser).callonOperatorLessOrEqualNode1, expr: &litMatcher{ - pos: position{line: 129, col: 5, offset: 3016}, + pos: position{line: 132, col: 5, offset: 3058}, val: "<=", ignoreCase: false, want: "\"<=\"", @@ -675,12 +655,12 @@ var g = &grammar{ }, { name: "OperatorGreaterNode", - pos: position{line: 133, col: 1, offset: 3078}, + pos: position{line: 136, col: 1, offset: 3120}, expr: &actionExpr{ - pos: position{line: 134, col: 5, offset: 3105}, + pos: position{line: 137, col: 5, offset: 3147}, run: (*parser).callonOperatorGreaterNode1, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3105}, + pos: position{line: 137, col: 5, offset: 3147}, val: ">", ignoreCase: false, want: "\">\"", @@ -689,12 +669,12 @@ var g = &grammar{ }, { name: "OperatorGreaterOrEqualNode", - pos: position{line: 138, col: 1, offset: 3166}, + pos: position{line: 141, col: 1, offset: 3208}, expr: &actionExpr{ - pos: position{line: 139, col: 5, offset: 3200}, + pos: position{line: 142, col: 5, offset: 3242}, run: (*parser).callonOperatorGreaterOrEqualNode1, expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3200}, + pos: position{line: 142, col: 5, offset: 3242}, val: ">=", ignoreCase: false, want: "\">=\"", @@ -703,27 +683,27 @@ var g = &grammar{ }, { name: "TimeYear", - pos: position{line: 148, col: 1, offset: 3386}, + pos: position{line: 151, col: 1, offset: 3428}, expr: &actionExpr{ - pos: position{line: 149, col: 5, offset: 3402}, + pos: position{line: 152, col: 5, offset: 3444}, run: (*parser).callonTimeYear1, expr: &seqExpr{ - pos: position{line: 149, col: 5, offset: 3402}, + pos: position{line: 152, col: 5, offset: 3444}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 149, col: 5, offset: 3402}, + pos: position{line: 152, col: 5, offset: 3444}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 149, col: 11, offset: 3408}, + pos: position{line: 152, col: 11, offset: 3450}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 149, col: 17, offset: 3414}, + pos: position{line: 152, col: 17, offset: 3456}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 149, col: 23, offset: 3420}, + pos: position{line: 152, col: 23, offset: 3462}, name: "Digit", }, }, @@ -732,19 +712,19 @@ var g = &grammar{ }, { name: "TimeMonth", - pos: position{line: 153, col: 1, offset: 3462}, + pos: position{line: 156, col: 1, offset: 3504}, expr: &actionExpr{ - pos: position{line: 154, col: 5, offset: 3479}, + pos: position{line: 157, col: 5, offset: 3521}, run: (*parser).callonTimeMonth1, expr: &seqExpr{ - pos: position{line: 154, col: 5, offset: 3479}, + pos: position{line: 157, col: 5, offset: 3521}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 154, col: 5, offset: 3479}, + pos: position{line: 157, col: 5, offset: 3521}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 154, col: 11, offset: 3485}, + pos: position{line: 157, col: 11, offset: 3527}, name: "Digit", }, }, @@ -753,19 +733,19 @@ var g = &grammar{ }, { name: "TimeDay", - pos: position{line: 158, col: 1, offset: 3527}, + pos: position{line: 161, col: 1, offset: 3569}, expr: &actionExpr{ - pos: position{line: 159, col: 5, offset: 3542}, + pos: position{line: 162, col: 5, offset: 3584}, run: (*parser).callonTimeDay1, expr: &seqExpr{ - pos: position{line: 159, col: 5, offset: 3542}, + pos: position{line: 162, col: 5, offset: 3584}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 159, col: 5, offset: 3542}, + pos: position{line: 162, col: 5, offset: 3584}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 159, col: 11, offset: 3548}, + pos: position{line: 162, col: 11, offset: 3590}, name: "Digit", }, }, @@ -774,19 +754,19 @@ var g = &grammar{ }, { name: "TimeHour", - pos: position{line: 163, col: 1, offset: 3590}, + pos: position{line: 166, col: 1, offset: 3632}, expr: &actionExpr{ - pos: position{line: 164, col: 5, offset: 3606}, + pos: position{line: 167, col: 5, offset: 3648}, run: (*parser).callonTimeHour1, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3606}, + pos: position{line: 167, col: 5, offset: 3648}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 164, col: 5, offset: 3606}, + pos: position{line: 167, col: 5, offset: 3648}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 164, col: 11, offset: 3612}, + pos: position{line: 167, col: 11, offset: 3654}, name: "Digit", }, }, @@ -795,19 +775,19 @@ var g = &grammar{ }, { name: "TimeMinute", - pos: position{line: 168, col: 1, offset: 3654}, + pos: position{line: 171, col: 1, offset: 3696}, expr: &actionExpr{ - pos: position{line: 169, col: 5, offset: 3672}, + pos: position{line: 172, col: 5, offset: 3714}, run: (*parser).callonTimeMinute1, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3672}, + pos: position{line: 172, col: 5, offset: 3714}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 169, col: 5, offset: 3672}, + pos: position{line: 172, col: 5, offset: 3714}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 169, col: 11, offset: 3678}, + pos: position{line: 172, col: 11, offset: 3720}, name: "Digit", }, }, @@ -816,19 +796,19 @@ var g = &grammar{ }, { name: "TimeSecond", - pos: position{line: 173, col: 1, offset: 3720}, + pos: position{line: 176, col: 1, offset: 3762}, expr: &actionExpr{ - pos: position{line: 174, col: 5, offset: 3738}, + pos: position{line: 177, col: 5, offset: 3780}, run: (*parser).callonTimeSecond1, expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 3738}, + pos: position{line: 177, col: 5, offset: 3780}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 174, col: 5, offset: 3738}, + pos: position{line: 177, col: 5, offset: 3780}, name: "Digit", }, &ruleRefExpr{ - pos: position{line: 174, col: 11, offset: 3744}, + pos: position{line: 177, col: 11, offset: 3786}, name: "Digit", }, }, @@ -837,35 +817,35 @@ var g = &grammar{ }, { name: "FullDate", - pos: position{line: 178, col: 1, offset: 3786}, + pos: position{line: 181, col: 1, offset: 3828}, expr: &actionExpr{ - pos: position{line: 179, col: 5, offset: 3802}, + pos: position{line: 182, col: 5, offset: 3844}, run: (*parser).callonFullDate1, expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 3802}, + pos: position{line: 182, col: 5, offset: 3844}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 179, col: 5, offset: 3802}, + pos: position{line: 182, col: 5, offset: 3844}, name: "TimeYear", }, &litMatcher{ - pos: position{line: 179, col: 14, offset: 3811}, + pos: position{line: 182, col: 14, offset: 3853}, val: "-", ignoreCase: false, want: "\"-\"", }, &ruleRefExpr{ - pos: position{line: 179, col: 18, offset: 3815}, + pos: position{line: 182, col: 18, offset: 3857}, name: "TimeMonth", }, &litMatcher{ - pos: position{line: 179, col: 28, offset: 3825}, + pos: position{line: 182, col: 28, offset: 3867}, val: "-", ignoreCase: false, want: "\"-\"", }, &ruleRefExpr{ - pos: position{line: 179, col: 32, offset: 3829}, + pos: position{line: 182, col: 32, offset: 3871}, name: "TimeDay", }, }, @@ -874,52 +854,52 @@ var g = &grammar{ }, { name: "FullTime", - pos: position{line: 183, col: 1, offset: 3873}, + pos: position{line: 186, col: 1, offset: 3915}, expr: &actionExpr{ - pos: position{line: 184, col: 5, offset: 3889}, + pos: position{line: 187, col: 5, offset: 3931}, run: (*parser).callonFullTime1, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 3889}, + pos: position{line: 187, col: 5, offset: 3931}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 184, col: 5, offset: 3889}, + pos: position{line: 187, col: 5, offset: 3931}, name: "TimeHour", }, &litMatcher{ - pos: position{line: 184, col: 14, offset: 3898}, + pos: position{line: 187, col: 14, offset: 3940}, val: ":", ignoreCase: false, want: "\":\"", }, &ruleRefExpr{ - pos: position{line: 184, col: 18, offset: 3902}, + pos: position{line: 187, col: 18, offset: 3944}, name: "TimeMinute", }, &litMatcher{ - pos: position{line: 184, col: 29, offset: 3913}, + pos: position{line: 187, col: 29, offset: 3955}, val: ":", ignoreCase: false, want: "\":\"", }, &ruleRefExpr{ - pos: position{line: 184, col: 33, offset: 3917}, + pos: position{line: 187, col: 33, offset: 3959}, name: "TimeSecond", }, &zeroOrOneExpr{ - pos: position{line: 184, col: 44, offset: 3928}, + pos: position{line: 187, col: 44, offset: 3970}, expr: &seqExpr{ - pos: position{line: 184, col: 45, offset: 3929}, + pos: position{line: 187, col: 45, offset: 3971}, exprs: []any{ &litMatcher{ - pos: position{line: 184, col: 45, offset: 3929}, + pos: position{line: 187, col: 45, offset: 3971}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 184, col: 49, offset: 3933}, + pos: position{line: 187, col: 49, offset: 3975}, expr: &ruleRefExpr{ - pos: position{line: 184, col: 49, offset: 3933}, + pos: position{line: 187, col: 49, offset: 3975}, name: "Digit", }, }, @@ -927,28 +907,28 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 184, col: 59, offset: 3943}, + pos: position{line: 187, col: 59, offset: 3985}, alternatives: []any{ &litMatcher{ - pos: position{line: 184, col: 59, offset: 3943}, + pos: position{line: 187, col: 59, offset: 3985}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 184, col: 65, offset: 3949}, + pos: position{line: 187, col: 65, offset: 3991}, exprs: []any{ &choiceExpr{ - pos: position{line: 184, col: 66, offset: 3950}, + pos: position{line: 187, col: 66, offset: 3992}, alternatives: []any{ &litMatcher{ - pos: position{line: 184, col: 66, offset: 3950}, + pos: position{line: 187, col: 66, offset: 3992}, val: "+", ignoreCase: false, want: "\"+\"", }, &litMatcher{ - pos: position{line: 184, col: 72, offset: 3956}, + pos: position{line: 187, col: 72, offset: 3998}, val: "-", ignoreCase: false, want: "\"-\"", @@ -956,17 +936,17 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 184, col: 77, offset: 3961}, + pos: position{line: 187, col: 77, offset: 4003}, name: "TimeHour", }, &litMatcher{ - pos: position{line: 184, col: 86, offset: 3970}, + pos: position{line: 187, col: 86, offset: 4012}, val: ":", ignoreCase: false, want: "\":\"", }, &ruleRefExpr{ - pos: position{line: 184, col: 90, offset: 3974}, + pos: position{line: 187, col: 90, offset: 4016}, name: "TimeMinute", }, }, @@ -979,25 +959,25 @@ var g = &grammar{ }, { name: "DateTime", - pos: position{line: 188, col: 1, offset: 4022}, + pos: position{line: 191, col: 1, offset: 4064}, expr: &actionExpr{ - pos: position{line: 189, col: 5, offset: 4035}, + pos: position{line: 192, col: 5, offset: 4077}, run: (*parser).callonDateTime1, expr: &seqExpr{ - pos: position{line: 189, col: 5, offset: 4035}, + pos: position{line: 192, col: 5, offset: 4077}, exprs: []any{ &ruleRefExpr{ - pos: position{line: 189, col: 5, offset: 4035}, + pos: position{line: 192, col: 5, offset: 4077}, name: "FullDate", }, &litMatcher{ - pos: position{line: 189, col: 14, offset: 4044}, + pos: position{line: 192, col: 14, offset: 4086}, val: "T", ignoreCase: false, want: "\"T\"", }, &ruleRefExpr{ - pos: position{line: 189, col: 18, offset: 4048}, + pos: position{line: 192, col: 18, offset: 4090}, name: "FullTime", }, }, @@ -1006,12 +986,12 @@ var g = &grammar{ }, { name: "Char", - pos: position{line: 197, col: 1, offset: 4214}, + pos: position{line: 200, col: 1, offset: 4256}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4226}, + pos: position{line: 201, col: 5, offset: 4268}, run: (*parser).callonChar1, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4226}, + pos: position{line: 201, col: 5, offset: 4268}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1021,26 +1001,26 @@ var g = &grammar{ }, { name: "String", - pos: position{line: 202, col: 1, offset: 4271}, + pos: position{line: 205, col: 1, offset: 4313}, expr: &actionExpr{ - pos: position{line: 203, col: 5, offset: 4285}, + pos: position{line: 206, col: 5, offset: 4327}, run: (*parser).callonString1, expr: &seqExpr{ - pos: position{line: 203, col: 5, offset: 4285}, + pos: position{line: 206, col: 5, offset: 4327}, exprs: []any{ &litMatcher{ - pos: position{line: 203, col: 5, offset: 4285}, + pos: position{line: 206, col: 5, offset: 4327}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 203, col: 9, offset: 4289}, + pos: position{line: 206, col: 9, offset: 4331}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 203, col: 11, offset: 4291}, + pos: position{line: 206, col: 11, offset: 4333}, expr: &charClassMatcher{ - pos: position{line: 203, col: 11, offset: 4291}, + pos: position{line: 206, col: 11, offset: 4333}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1049,7 +1029,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 203, col: 17, offset: 4297}, + pos: position{line: 206, col: 17, offset: 4339}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1060,12 +1040,12 @@ var g = &grammar{ }, { name: "Digit", - pos: position{line: 207, col: 1, offset: 4332}, + pos: position{line: 210, col: 1, offset: 4374}, expr: &actionExpr{ - pos: position{line: 208, col: 5, offset: 4345}, + pos: position{line: 211, col: 5, offset: 4387}, run: (*parser).callonDigit1, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4345}, + pos: position{line: 211, col: 5, offset: 4387}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1075,41 +1055,34 @@ var g = &grammar{ }, { name: "_", - pos: position{line: 212, col: 1, offset: 4387}, - expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4396}, - expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4396}, - val: "[ \\t]", - chars: []rune{' ', '\t'}, - ignoreCase: false, - inverted: false, + pos: position{line: 215, col: 1, offset: 4429}, + expr: &actionExpr{ + pos: position{line: 216, col: 5, offset: 4438}, + run: (*parser).callon_1, + expr: &zeroOrMoreExpr{ + pos: position{line: 216, col: 5, offset: 4438}, + expr: &charClassMatcher{ + pos: position{line: 216, col: 5, offset: 4438}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, }, }, }, }, } -func (c *current) onAST1(nodes any) (any, error) { - return buildAST(nodes, c.text, c.pos) +func (c *current) onAST1(n any) (any, error) { + return buildAST(n, c.text, c.pos) } func (p *parser) callonAST1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onAST1(stack["nodes"]) -} - -func (c *current) onNodes1(head, tail any) (any, error) { - return buildNodes(head, tail) - -} - -func (p *parser) callonNodes1() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNodes1(stack["head"], stack["tail"]) + return p.cur.onAST1(stack["n"]) } func (c *current) onGroupNode1(k, v any) (any, error) { @@ -1409,6 +1382,17 @@ func (p *parser) callonDigit1() (any, error) { return p.cur.onDigit1() } +func (c *current) on_1() (any, error) { + return nil, nil + +} + +func (p *parser) callon_1() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.on_1() +} + var ( // errNoRule is returned when the grammar to parse has no rule. errNoRule = errors.New("grammar has no rule") diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index 7421f524541..a77d45f2c96 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -517,7 +517,7 @@ func TestParse(t *testing.T) { Key: "author", Nodes: []ast.Node{ &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "Jane Smith"}, }, }, @@ -582,7 +582,7 @@ func TestParse(t *testing.T) { Key: "author", Nodes: []ast.Node{ &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "Jane"}, }, }, @@ -606,7 +606,7 @@ func TestParse(t *testing.T) { Key: "author", Nodes: []ast.Node{ &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "Jane"}, }, }, @@ -845,7 +845,7 @@ func TestParse(t *testing.T) { } if diff := test.DiffAst(tt.expectedAst, parsedAST); diff != "" { - t.Fatalf("AST mismatch \nquery: '%s' \n(-want +got): %s", q, diff) + t.Fatalf("AST mismatch \nquery: '%s' \n(-expected +got): %s", q, diff) } }) } diff --git a/services/search/pkg/query/kql/factory.go b/services/search/pkg/query/kql/factory.go index bdee43a8873..6b26f1f685a 100644 --- a/services/search/pkg/query/kql/factory.go +++ b/services/search/pkg/query/kql/factory.go @@ -40,35 +40,10 @@ func buildAST(n interface{}, text []byte, pos position) (*ast.Ast, error) { return &ast.Ast{ Base: b, - Nodes: nodes, + Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolOR}, nodes...), }, nil } -func buildNodes(head, tail interface{}) ([]ast.Node, error) { - headNode, err := toNode[ast.Node](head) - if err != nil { - return nil, err - } - - if tail == nil { - return []ast.Node{headNode}, nil - } - - tailNodes, err := toNodes[ast.Node](tail) - if err != nil { - return nil, err - } - - allNodes := []ast.Node{headNode} - - connectionNode := incorporateNode(headNode, tailNodes...) - if connectionNode != nil { - allNodes = append(allNodes, connectionNode) - } - - return append(allNodes, tailNodes...), nil -} - func buildStringNode(k, v interface{}, text []byte, pos position) (*ast.StringNode, error) { b, err := base(text, pos) if err != nil { @@ -184,6 +159,6 @@ func buildGroupNode(k, n interface{}, text []byte, pos position) (*ast.GroupNode return &ast.GroupNode{ Base: b, Key: key, - Nodes: nodes, + Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolAND}, nodes...), }, nil } diff --git a/services/search/pkg/query/kql/kql.go b/services/search/pkg/query/kql/kql.go index 48c349e817c..67565f59533 100644 --- a/services/search/pkg/query/kql/kql.go +++ b/services/search/pkg/query/kql/kql.go @@ -3,7 +3,6 @@ package kql import ( "errors" - "strings" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -39,113 +38,3 @@ func (b Builder) Build(q string) (*ast.Ast, error) { return f.(*ast.Ast), nil } - -// incorporateNode connects a leading node with the rest -func incorporateNode(headNode ast.Node, tailNodes ...ast.Node) *ast.OperatorNode { - switch headNode.(type) { - case *ast.OperatorNode: - return nil - } - - var nearestNeighborNode ast.Node - var nearestNeighborOperators []*ast.OperatorNode - -l: - for _, tailNode := range tailNodes { - switch node := tailNode.(type) { - case *ast.OperatorNode: - nearestNeighborOperators = append(nearestNeighborOperators, node) - default: - nearestNeighborNode = node - break l - } - } - - if nearestNeighborNode == nil { - return nil - } - - headKey := strings.ToLower(nodeKey(headNode)) - neighborKey := strings.ToLower(nodeKey(nearestNeighborNode)) - - connection := &ast.OperatorNode{ - Base: &ast.Base{Loc: &ast.Location{Source: &[]string{"implicitly operator"}[0]}}, - Value: BoolAND, - } - - // if the current node and the neighbor node have the same key - // the connection is of type OR, same applies if no keys are in place - // - // "" == "" - // - // spec: same - // author:"John Smith" author:"Jane Smith" - // author:"John Smith" OR author:"Jane Smith" - if headKey == neighborKey { - connection.Value = BoolOR - } - - // decisions based on nearest neighbor node - switch nearestNeighborNode.(type) { - // nearest neighbor node type could change the default case - // docs says, if the next value node: - // - // is a group AND has no key - // - // even if the current node has none too, which normal leads to SAME KEY OR - // - // it should be an AND edge - // - // spec: same - // cat (dog OR fox) - // cat AND (dog OR fox) - // - // note: - // sounds contradictory to me - case *ast.GroupNode: - if headKey == "" && neighborKey == "" { - connection.Value = BoolAND - } - } - - // decisions based on nearest neighbor operators - for i, node := range nearestNeighborOperators { - // consider direct neighbor operator only - if i == 0 { - // no connection is necessary here because an `AND` or `OR` edge is already present - // exit - for _, skipValue := range []string{BoolOR, BoolAND} { - if node.Value == skipValue { - return nil - } - } - - // if neighbor node negotiates, AND edge is needed - // - // spec: same - // cat -dog - // cat AND NOT dog - if node.Value == BoolNOT { - connection.Value = BoolAND - } - } - } - - return connection -} - -// nodeKey tries to return a node key -func nodeKey(n ast.Node) string { - switch node := n.(type) { - case *ast.StringNode: - return node.Key - case *ast.DateTimeNode: - return node.Key - case *ast.BooleanNode: - return node.Key - case *ast.GroupNode: - return node.Key - default: - return "" - } -} From 8235ff7bd9ccb4fdfd46673b06948b8e8700b13c Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Sat, 9 Sep 2023 23:20:05 +0200 Subject: [PATCH 05/11] enhancement: explicit error handling for falsy group nodes and queries with leading binary operator --- services/search/pkg/query/kql/connect.go | 19 +- services/search/pkg/query/kql/dictionary.peg | 5 +- .../search/pkg/query/kql/dictionary_gen.go | 3562 ++++++++++------- .../search/pkg/query/kql/dictionary_test.go | 109 +- services/search/pkg/query/kql/factory.go | 46 +- services/search/pkg/query/kql/kql.go | 4 + services/search/pkg/query/kql/kql_test.go | 32 +- 7 files changed, 2340 insertions(+), 1437 deletions(-) diff --git a/services/search/pkg/query/kql/connect.go b/services/search/pkg/query/kql/connect.go index 4f7dce66672..e592de8544a 100644 --- a/services/search/pkg/query/kql/connect.go +++ b/services/search/pkg/query/kql/connect.go @@ -66,8 +66,8 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections return nil } - headKey := strings.ToLower(c.nodeKey(head)) - neighborKey := strings.ToLower(c.nodeKey(neighbor)) + headKey := strings.ToLower(ast.NodeKey(head)) + neighborKey := strings.ToLower(ast.NodeKey(neighbor)) connection := &ast.OperatorNode{ Base: &ast.Base{Loc: &ast.Location{Source: &[]string{"implicitly operator"}[0]}}, @@ -141,18 +141,3 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections return []ast.Node{connection} } - -func (c DefaultConnector) nodeKey(n ast.Node) string { - switch node := n.(type) { - case *ast.StringNode: - return node.Key - case *ast.DateTimeNode: - return node.Key - case *ast.BooleanNode: - return node.Key - case *ast.GroupNode: - return node.Key - default: - return "" - } -} diff --git a/services/search/pkg/query/kql/dictionary.peg b/services/search/pkg/query/kql/dictionary.peg index 999d25bb90b..ca9658dfa53 100644 --- a/services/search/pkg/query/kql/dictionary.peg +++ b/services/search/pkg/query/kql/dictionary.peg @@ -7,10 +7,7 @@ //////////////////////////////////////////////////////// AST <- - _ !( - OperatorBooleanAndNode / - OperatorBooleanOrNode - ) n:Nodes { + n:Nodes { return buildAST(n, c.text, c.pos) } diff --git a/services/search/pkg/query/kql/dictionary_gen.go b/services/search/pkg/query/kql/dictionary_gen.go index ba3f637b3f1..afce6c5c85e 100644 --- a/services/search/pkg/query/kql/dictionary_gen.go +++ b/services/search/pkg/query/kql/dictionary_gen.go @@ -12,7 +12,6 @@ import ( "sort" "strconv" "strings" - "sync" "unicode" "unicode/utf8" ) @@ -25,55 +24,40 @@ var g = &grammar{ expr: &actionExpr{ pos: position{line: 10, col: 5, offset: 154}, run: (*parser).callonAST1, - expr: &seqExpr{ - pos: position{line: 10, col: 5, offset: 154}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 10, col: 5, offset: 154}, - name: "_", - }, - ¬Expr{ - pos: position{line: 10, col: 7, offset: 156}, - expr: &choiceExpr{ - pos: position{line: 11, col: 9, offset: 167}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 11, col: 9, offset: 167}, - name: "OperatorBooleanAndNode", - }, - &ruleRefExpr{ - pos: position{line: 12, col: 9, offset: 200}, - name: "OperatorBooleanOrNode", - }, - }, - }, - }, - &labeledExpr{ - pos: position{line: 13, col: 7, offset: 228}, - label: "n", - expr: &ruleRefExpr{ - pos: position{line: 13, col: 9, offset: 230}, - name: "Nodes", - }, - }, + expr: &labeledExpr{ + pos: position{line: 10, col: 5, offset: 154}, + label: "n", + expr: &ruleRefExpr{ + pos: position{line: 10, col: 7, offset: 156}, + name: "Nodes", }, }, }, }, { name: "Nodes", - pos: position{line: 21, col: 1, offset: 411}, + pos: position{line: 18, col: 1, offset: 337}, expr: &oneOrMoreExpr{ - pos: position{line: 22, col: 5, offset: 424}, + pos: position{line: 19, col: 5, offset: 350}, expr: &seqExpr{ - pos: position{line: 22, col: 6, offset: 425}, + pos: position{line: 19, col: 6, offset: 351}, exprs: []any{ - &ruleRefExpr{ - pos: position{line: 22, col: 6, offset: 425}, - name: "_", + &actionExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + run: (*parser).callonNodes3, + expr: &zeroOrMoreExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + expr: &charClassMatcher{ + pos: position{line: 213, col: 5, offset: 4364}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, + }, }, &ruleRefExpr{ - pos: position{line: 22, col: 8, offset: 427}, + pos: position{line: 19, col: 8, offset: 353}, name: "Node", }, }, @@ -82,261 +66,1297 @@ var g = &grammar{ }, { name: "Node", - pos: position{line: 24, col: 1, offset: 435}, + pos: position{line: 21, col: 1, offset: 361}, expr: &choiceExpr{ - pos: position{line: 25, col: 5, offset: 447}, + pos: position{line: 22, col: 5, offset: 373}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 25, col: 5, offset: 447}, + pos: position{line: 22, col: 5, offset: 373}, name: "GroupNode", }, - &ruleRefExpr{ - pos: position{line: 26, col: 5, offset: 463}, - name: "PropertyRestrictionNodes", - }, - &ruleRefExpr{ - pos: position{line: 27, col: 5, offset: 494}, - name: "OperatorBooleanNodes", - }, - &ruleRefExpr{ - pos: position{line: 28, col: 5, offset: 521}, - name: "FreeTextKeywordNodes", - }, - }, - }, - }, - { - name: "GroupNode", - pos: position{line: 34, col: 1, offset: 669}, - expr: &actionExpr{ - pos: position{line: 35, col: 5, offset: 686}, - run: (*parser).callonGroupNode1, - expr: &seqExpr{ - pos: position{line: 35, col: 5, offset: 686}, - exprs: []any{ - &labeledExpr{ - pos: position{line: 35, col: 5, offset: 686}, - label: "k", - expr: &zeroOrOneExpr{ - pos: position{line: 35, col: 7, offset: 688}, - expr: &oneOrMoreExpr{ - pos: position{line: 35, col: 8, offset: 689}, - expr: &ruleRefExpr{ - pos: position{line: 35, col: 8, offset: 689}, - name: "Char", + &actionExpr{ + pos: position{line: 46, col: 5, offset: 1042}, + run: (*parser).callonNode3, + expr: &seqExpr{ + pos: position{line: 46, col: 5, offset: 1042}, + exprs: []any{ + &labeledExpr{ + pos: position{line: 46, col: 5, offset: 1042}, + label: "k", + expr: &oneOrMoreExpr{ + pos: position{line: 46, col: 7, offset: 1044}, + expr: &actionExpr{ + pos: position{line: 198, col: 5, offset: 4194}, + run: (*parser).callonNode7, + expr: &charClassMatcher{ + pos: position{line: 198, col: 5, offset: 4194}, + val: "[A-Za-z]", + ranges: []rune{'A', 'Z', 'a', 'z'}, + ignoreCase: false, + inverted: false, + }, + }, }, }, - }, - }, - &zeroOrOneExpr{ - pos: position{line: 35, col: 16, offset: 697}, - expr: &choiceExpr{ - pos: position{line: 35, col: 17, offset: 698}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 35, col: 17, offset: 698}, - name: "OperatorColonNode", + &choiceExpr{ + pos: position{line: 46, col: 14, offset: 1051}, + alternatives: []any{ + &actionExpr{ + pos: position{line: 114, col: 5, offset: 2721}, + run: (*parser).callonNode10, + expr: &litMatcher{ + pos: position{line: 114, col: 5, offset: 2721}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + }, + &actionExpr{ + pos: position{line: 119, col: 5, offset: 2807}, + run: (*parser).callonNode12, + expr: &litMatcher{ + pos: position{line: 119, col: 5, offset: 2807}, + val: "=", + ignoreCase: false, + want: "\"=\"", + }, + }, }, - &ruleRefExpr{ - pos: position{line: 35, col: 37, offset: 718}, - name: "OperatorEqualNode", + }, + &labeledExpr{ + pos: position{line: 46, col: 53, offset: 1090}, + label: "v", + expr: &choiceExpr{ + pos: position{line: 46, col: 56, offset: 1093}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 46, col: 56, offset: 1093}, + val: "true", + ignoreCase: false, + want: "\"true\"", + }, + &litMatcher{ + pos: position{line: 46, col: 65, offset: 1102}, + val: "false", + ignoreCase: false, + want: "\"false\"", + }, + }, }, }, }, }, - &litMatcher{ - pos: position{line: 35, col: 57, offset: 738}, - val: "(", - ignoreCase: false, - want: "\"(\"", - }, - &labeledExpr{ - pos: position{line: 35, col: 61, offset: 742}, - label: "v", - expr: &ruleRefExpr{ - pos: position{line: 35, col: 63, offset: 744}, - name: "Nodes", + }, + &actionExpr{ + pos: position{line: 51, col: 5, offset: 1203}, + run: (*parser).callonNode18, + expr: &seqExpr{ + pos: position{line: 51, col: 5, offset: 1203}, + exprs: []any{ + &labeledExpr{ + pos: position{line: 51, col: 5, offset: 1203}, + label: "k", + expr: &oneOrMoreExpr{ + pos: position{line: 51, col: 7, offset: 1205}, + expr: &actionExpr{ + pos: position{line: 198, col: 5, offset: 4194}, + run: (*parser).callonNode22, + expr: &charClassMatcher{ + pos: position{line: 198, col: 5, offset: 4194}, + val: "[A-Za-z]", + ranges: []rune{'A', 'Z', 'a', 'z'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + &labeledExpr{ + pos: position{line: 51, col: 13, offset: 1211}, + label: "o", + expr: &choiceExpr{ + pos: position{line: 52, col: 9, offset: 1223}, + alternatives: []any{ + &actionExpr{ + pos: position{line: 139, col: 5, offset: 3168}, + run: (*parser).callonNode26, + expr: &litMatcher{ + pos: position{line: 139, col: 5, offset: 3168}, + val: ">=", + ignoreCase: false, + want: "\">=\"", + }, + }, + &actionExpr{ + pos: position{line: 129, col: 5, offset: 2984}, + run: (*parser).callonNode28, + expr: &litMatcher{ + pos: position{line: 129, col: 5, offset: 2984}, + val: "<=", + ignoreCase: false, + want: "\"<=\"", + }, + }, + &actionExpr{ + pos: position{line: 134, col: 5, offset: 3073}, + run: (*parser).callonNode30, + expr: &litMatcher{ + pos: position{line: 134, col: 5, offset: 3073}, + val: ">", + ignoreCase: false, + want: "\">\"", + }, + }, + &actionExpr{ + pos: position{line: 124, col: 5, offset: 2892}, + run: (*parser).callonNode32, + expr: &litMatcher{ + pos: position{line: 124, col: 5, offset: 2892}, + val: "<", + ignoreCase: false, + want: "\"<\"", + }, + }, + &actionExpr{ + pos: position{line: 119, col: 5, offset: 2807}, + run: (*parser).callonNode34, + expr: &litMatcher{ + pos: position{line: 119, col: 5, offset: 2807}, + val: "=", + ignoreCase: false, + want: "\"=\"", + }, + }, + &actionExpr{ + pos: position{line: 114, col: 5, offset: 2721}, + run: (*parser).callonNode36, + expr: &litMatcher{ + pos: position{line: 114, col: 5, offset: 2721}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 58, col: 7, offset: 1403}, + expr: &litMatcher{ + pos: position{line: 58, col: 7, offset: 1403}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + &labeledExpr{ + pos: position{line: 58, col: 12, offset: 1408}, + label: "v", + expr: &choiceExpr{ + pos: position{line: 59, col: 9, offset: 1420}, + alternatives: []any{ + &actionExpr{ + pos: position{line: 189, col: 5, offset: 4003}, + run: (*parser).callonNode42, + expr: &seqExpr{ + pos: position{line: 189, col: 5, offset: 4003}, + exprs: []any{ + &actionExpr{ + pos: position{line: 179, col: 5, offset: 3770}, + run: (*parser).callonNode44, + expr: &seqExpr{ + pos: position{line: 179, col: 5, offset: 3770}, + exprs: []any{ + &actionExpr{ + pos: position{line: 149, col: 5, offset: 3370}, + run: (*parser).callonNode46, + expr: &seqExpr{ + pos: position{line: 149, col: 5, offset: 3370}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode48, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode50, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode52, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode54, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 179, col: 14, offset: 3779}, + val: "-", + ignoreCase: false, + want: "\"-\"", + }, + &actionExpr{ + pos: position{line: 154, col: 5, offset: 3447}, + run: (*parser).callonNode57, + expr: &seqExpr{ + pos: position{line: 154, col: 5, offset: 3447}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode59, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode61, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 179, col: 28, offset: 3793}, + val: "-", + ignoreCase: false, + want: "\"-\"", + }, + &actionExpr{ + pos: position{line: 159, col: 5, offset: 3510}, + run: (*parser).callonNode64, + expr: &seqExpr{ + pos: position{line: 159, col: 5, offset: 3510}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode66, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode68, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 189, col: 14, offset: 4012}, + val: "T", + ignoreCase: false, + want: "\"T\"", + }, + &actionExpr{ + pos: position{line: 184, col: 5, offset: 3857}, + run: (*parser).callonNode71, + expr: &seqExpr{ + pos: position{line: 184, col: 5, offset: 3857}, + exprs: []any{ + &actionExpr{ + pos: position{line: 164, col: 5, offset: 3574}, + run: (*parser).callonNode73, + expr: &seqExpr{ + pos: position{line: 164, col: 5, offset: 3574}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode75, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode77, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 184, col: 14, offset: 3866}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + &actionExpr{ + pos: position{line: 169, col: 5, offset: 3640}, + run: (*parser).callonNode80, + expr: &seqExpr{ + pos: position{line: 169, col: 5, offset: 3640}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode82, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode84, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 184, col: 29, offset: 3881}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + &actionExpr{ + pos: position{line: 174, col: 5, offset: 3706}, + run: (*parser).callonNode87, + expr: &seqExpr{ + pos: position{line: 174, col: 5, offset: 3706}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode89, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode91, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 184, col: 44, offset: 3896}, + expr: &seqExpr{ + pos: position{line: 184, col: 45, offset: 3897}, + exprs: []any{ + &litMatcher{ + pos: position{line: 184, col: 45, offset: 3897}, + val: ".", + ignoreCase: false, + want: "\".\"", + }, + &oneOrMoreExpr{ + pos: position{line: 184, col: 49, offset: 3901}, + expr: &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode97, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + }, + &choiceExpr{ + pos: position{line: 184, col: 59, offset: 3911}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 184, col: 59, offset: 3911}, + val: "Z", + ignoreCase: false, + want: "\"Z\"", + }, + &seqExpr{ + pos: position{line: 184, col: 65, offset: 3917}, + exprs: []any{ + &charClassMatcher{ + pos: position{line: 184, col: 66, offset: 3918}, + val: "[+-]", + chars: []rune{'+', '-'}, + ignoreCase: false, + inverted: false, + }, + &actionExpr{ + pos: position{line: 164, col: 5, offset: 3574}, + run: (*parser).callonNode103, + expr: &seqExpr{ + pos: position{line: 164, col: 5, offset: 3574}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode105, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode107, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 184, col: 86, offset: 3938}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + &actionExpr{ + pos: position{line: 169, col: 5, offset: 3640}, + run: (*parser).callonNode110, + expr: &seqExpr{ + pos: position{line: 169, col: 5, offset: 3640}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode112, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode114, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 179, col: 5, offset: 3770}, + run: (*parser).callonNode116, + expr: &seqExpr{ + pos: position{line: 179, col: 5, offset: 3770}, + exprs: []any{ + &actionExpr{ + pos: position{line: 149, col: 5, offset: 3370}, + run: (*parser).callonNode118, + expr: &seqExpr{ + pos: position{line: 149, col: 5, offset: 3370}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode120, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode122, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode124, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode126, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 179, col: 14, offset: 3779}, + val: "-", + ignoreCase: false, + want: "\"-\"", + }, + &actionExpr{ + pos: position{line: 154, col: 5, offset: 3447}, + run: (*parser).callonNode129, + expr: &seqExpr{ + pos: position{line: 154, col: 5, offset: 3447}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode131, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode133, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 179, col: 28, offset: 3793}, + val: "-", + ignoreCase: false, + want: "\"-\"", + }, + &actionExpr{ + pos: position{line: 159, col: 5, offset: 3510}, + run: (*parser).callonNode136, + expr: &seqExpr{ + pos: position{line: 159, col: 5, offset: 3510}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode138, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode140, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 184, col: 5, offset: 3857}, + run: (*parser).callonNode142, + expr: &seqExpr{ + pos: position{line: 184, col: 5, offset: 3857}, + exprs: []any{ + &actionExpr{ + pos: position{line: 164, col: 5, offset: 3574}, + run: (*parser).callonNode144, + expr: &seqExpr{ + pos: position{line: 164, col: 5, offset: 3574}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode146, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode148, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 184, col: 14, offset: 3866}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + &actionExpr{ + pos: position{line: 169, col: 5, offset: 3640}, + run: (*parser).callonNode151, + expr: &seqExpr{ + pos: position{line: 169, col: 5, offset: 3640}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode153, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode155, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 184, col: 29, offset: 3881}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + &actionExpr{ + pos: position{line: 174, col: 5, offset: 3706}, + run: (*parser).callonNode158, + expr: &seqExpr{ + pos: position{line: 174, col: 5, offset: 3706}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode160, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode162, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 184, col: 44, offset: 3896}, + expr: &seqExpr{ + pos: position{line: 184, col: 45, offset: 3897}, + exprs: []any{ + &litMatcher{ + pos: position{line: 184, col: 45, offset: 3897}, + val: ".", + ignoreCase: false, + want: "\".\"", + }, + &oneOrMoreExpr{ + pos: position{line: 184, col: 49, offset: 3901}, + expr: &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode168, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + }, + &choiceExpr{ + pos: position{line: 184, col: 59, offset: 3911}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 184, col: 59, offset: 3911}, + val: "Z", + ignoreCase: false, + want: "\"Z\"", + }, + &seqExpr{ + pos: position{line: 184, col: 65, offset: 3917}, + exprs: []any{ + &charClassMatcher{ + pos: position{line: 184, col: 66, offset: 3918}, + val: "[+-]", + chars: []rune{'+', '-'}, + ignoreCase: false, + inverted: false, + }, + &actionExpr{ + pos: position{line: 164, col: 5, offset: 3574}, + run: (*parser).callonNode174, + expr: &seqExpr{ + pos: position{line: 164, col: 5, offset: 3574}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode176, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode178, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 184, col: 86, offset: 3938}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + &actionExpr{ + pos: position{line: 169, col: 5, offset: 3640}, + run: (*parser).callonNode181, + expr: &seqExpr{ + pos: position{line: 169, col: 5, offset: 3640}, + exprs: []any{ + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode183, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + &actionExpr{ + pos: position{line: 208, col: 5, offset: 4313}, + run: (*parser).callonNode185, + expr: &charClassMatcher{ + pos: position{line: 208, col: 5, offset: 4313}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 62, col: 7, offset: 1473}, + expr: &litMatcher{ + pos: position{line: 62, col: 7, offset: 1473}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, }, }, - &litMatcher{ - pos: position{line: 35, col: 69, offset: 750}, - val: ")", - ignoreCase: false, - want: "\")\"", - }, - }, - }, - }, - }, - { - name: "PropertyRestrictionNodes", - pos: position{line: 43, col: 1, offset: 954}, - expr: &choiceExpr{ - pos: position{line: 44, col: 5, offset: 986}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 44, col: 5, offset: 986}, - name: "YesNoPropertyRestrictionNode", - }, - &ruleRefExpr{ - pos: position{line: 45, col: 5, offset: 1021}, - name: "DateTimeRestrictionNode", - }, - &ruleRefExpr{ - pos: position{line: 46, col: 5, offset: 1051}, - name: "TextPropertyRestrictionNode", }, - }, - }, - }, - { - name: "YesNoPropertyRestrictionNode", - pos: position{line: 48, col: 1, offset: 1080}, - expr: &actionExpr{ - pos: position{line: 49, col: 5, offset: 1116}, - run: (*parser).callonYesNoPropertyRestrictionNode1, - expr: &seqExpr{ - pos: position{line: 49, col: 5, offset: 1116}, - exprs: []any{ - &labeledExpr{ - pos: position{line: 49, col: 5, offset: 1116}, - label: "k", - expr: &oneOrMoreExpr{ - pos: position{line: 49, col: 7, offset: 1118}, - expr: &ruleRefExpr{ - pos: position{line: 49, col: 7, offset: 1118}, - name: "Char", + &actionExpr{ + pos: position{line: 67, col: 5, offset: 1579}, + run: (*parser).callonNode189, + expr: &seqExpr{ + pos: position{line: 67, col: 5, offset: 1579}, + exprs: []any{ + &labeledExpr{ + pos: position{line: 67, col: 5, offset: 1579}, + label: "k", + expr: &oneOrMoreExpr{ + pos: position{line: 67, col: 7, offset: 1581}, + expr: &actionExpr{ + pos: position{line: 198, col: 5, offset: 4194}, + run: (*parser).callonNode193, + expr: &charClassMatcher{ + pos: position{line: 198, col: 5, offset: 4194}, + val: "[A-Za-z]", + ranges: []rune{'A', 'Z', 'a', 'z'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + &choiceExpr{ + pos: position{line: 67, col: 14, offset: 1588}, + alternatives: []any{ + &actionExpr{ + pos: position{line: 114, col: 5, offset: 2721}, + run: (*parser).callonNode196, + expr: &litMatcher{ + pos: position{line: 114, col: 5, offset: 2721}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + }, + &actionExpr{ + pos: position{line: 119, col: 5, offset: 2807}, + run: (*parser).callonNode198, + expr: &litMatcher{ + pos: position{line: 119, col: 5, offset: 2807}, + val: "=", + ignoreCase: false, + want: "\"=\"", + }, + }, + }, + }, + &labeledExpr{ + pos: position{line: 67, col: 53, offset: 1627}, + label: "v", + expr: &choiceExpr{ + pos: position{line: 67, col: 56, offset: 1630}, + alternatives: []any{ + &actionExpr{ + pos: position{line: 203, col: 5, offset: 4253}, + run: (*parser).callonNode202, + expr: &seqExpr{ + pos: position{line: 203, col: 5, offset: 4253}, + exprs: []any{ + &litMatcher{ + pos: position{line: 203, col: 5, offset: 4253}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + &labeledExpr{ + pos: position{line: 203, col: 9, offset: 4257}, + label: "v", + expr: &zeroOrMoreExpr{ + pos: position{line: 203, col: 11, offset: 4259}, + expr: &charClassMatcher{ + pos: position{line: 203, col: 11, offset: 4259}, + val: "[^\"]", + chars: []rune{'"'}, + ignoreCase: false, + inverted: true, + }, + }, + }, + &litMatcher{ + pos: position{line: 203, col: 17, offset: 4265}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + }, + }, + &oneOrMoreExpr{ + pos: position{line: 67, col: 65, offset: 1639}, + expr: &charClassMatcher{ + pos: position{line: 67, col: 65, offset: 1639}, + val: "[^ ()]", + chars: []rune{' ', '(', ')'}, + ignoreCase: false, + inverted: true, + }, + }, + }, + }, }, }, }, - &choiceExpr{ - pos: position{line: 49, col: 14, offset: 1125}, + }, + &actionExpr{ + pos: position{line: 99, col: 5, offset: 2431}, + run: (*parser).callonNode211, + expr: &choiceExpr{ + pos: position{line: 99, col: 6, offset: 2432}, alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 49, col: 14, offset: 1125}, - name: "OperatorColonNode", + &litMatcher{ + pos: position{line: 99, col: 6, offset: 2432}, + val: "AND", + ignoreCase: false, + want: "\"AND\"", }, - &ruleRefExpr{ - pos: position{line: 49, col: 34, offset: 1145}, - name: "OperatorEqualNode", + &litMatcher{ + pos: position{line: 99, col: 14, offset: 2440}, + val: "+", + ignoreCase: false, + want: "\"+\"", }, }, }, - &labeledExpr{ - pos: position{line: 49, col: 53, offset: 1164}, - label: "v", - expr: &choiceExpr{ - pos: position{line: 49, col: 56, offset: 1167}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 49, col: 56, offset: 1167}, - val: "true", - ignoreCase: false, - want: "\"true\"", - }, - &litMatcher{ - pos: position{line: 49, col: 65, offset: 1176}, - val: "false", - ignoreCase: false, - want: "\"false\"", - }, + }, + &actionExpr{ + pos: position{line: 104, col: 5, offset: 2532}, + run: (*parser).callonNode215, + expr: &choiceExpr{ + pos: position{line: 104, col: 6, offset: 2533}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 104, col: 6, offset: 2533}, + val: "NOT", + ignoreCase: false, + want: "\"NOT\"", + }, + &litMatcher{ + pos: position{line: 104, col: 14, offset: 2541}, + val: "-", + ignoreCase: false, + want: "\"-\"", }, }, }, }, - }, - }, - }, - { - name: "DateTimeRestrictionNode", - pos: position{line: 53, col: 1, offset: 1246}, - expr: &actionExpr{ - pos: position{line: 54, col: 5, offset: 1277}, - run: (*parser).callonDateTimeRestrictionNode1, - expr: &seqExpr{ - pos: position{line: 54, col: 5, offset: 1277}, - exprs: []any{ - &labeledExpr{ - pos: position{line: 54, col: 5, offset: 1277}, - label: "k", - expr: &oneOrMoreExpr{ - pos: position{line: 54, col: 7, offset: 1279}, - expr: &ruleRefExpr{ - pos: position{line: 54, col: 7, offset: 1279}, - name: "Char", - }, - }, + &actionExpr{ + pos: position{line: 109, col: 5, offset: 2632}, + run: (*parser).callonNode219, + expr: &litMatcher{ + pos: position{line: 109, col: 6, offset: 2633}, + val: "OR", + ignoreCase: false, + want: "\"OR\"", }, - &labeledExpr{ - pos: position{line: 54, col: 13, offset: 1285}, - label: "o", - expr: &choiceExpr{ - pos: position{line: 55, col: 9, offset: 1297}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 55, col: 9, offset: 1297}, - name: "OperatorGreaterOrEqualNode", - }, - &ruleRefExpr{ - pos: position{line: 56, col: 9, offset: 1334}, - name: "OperatorLessOrEqualNode", + }, + &actionExpr{ + pos: position{line: 80, col: 6, offset: 1919}, + run: (*parser).callonNode221, + expr: &seqExpr{ + pos: position{line: 80, col: 6, offset: 1919}, + exprs: []any{ + &zeroOrOneExpr{ + pos: position{line: 80, col: 6, offset: 1919}, + expr: &actionExpr{ + pos: position{line: 114, col: 5, offset: 2721}, + run: (*parser).callonNode224, + expr: &litMatcher{ + pos: position{line: 114, col: 5, offset: 2721}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, }, - &ruleRefExpr{ - pos: position{line: 57, col: 9, offset: 1368}, - name: "OperatorGreaterNode", + }, + &actionExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + run: (*parser).callonNode226, + expr: &zeroOrMoreExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + expr: &charClassMatcher{ + pos: position{line: 213, col: 5, offset: 4364}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, }, - &ruleRefExpr{ - pos: position{line: 58, col: 9, offset: 1398}, - name: "OperatorLessNode", + }, + &labeledExpr{ + pos: position{line: 80, col: 27, offset: 1940}, + label: "v", + expr: &actionExpr{ + pos: position{line: 203, col: 5, offset: 4253}, + run: (*parser).callonNode230, + expr: &seqExpr{ + pos: position{line: 203, col: 5, offset: 4253}, + exprs: []any{ + &litMatcher{ + pos: position{line: 203, col: 5, offset: 4253}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + &labeledExpr{ + pos: position{line: 203, col: 9, offset: 4257}, + label: "v", + expr: &zeroOrMoreExpr{ + pos: position{line: 203, col: 11, offset: 4259}, + expr: &charClassMatcher{ + pos: position{line: 203, col: 11, offset: 4259}, + val: "[^\"]", + chars: []rune{'"'}, + ignoreCase: false, + inverted: true, + }, + }, + }, + &litMatcher{ + pos: position{line: 203, col: 17, offset: 4265}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + }, }, - &ruleRefExpr{ - pos: position{line: 59, col: 9, offset: 1425}, - name: "OperatorEqualNode", + }, + &actionExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + run: (*parser).callonNode237, + expr: &zeroOrMoreExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + expr: &charClassMatcher{ + pos: position{line: 213, col: 5, offset: 4364}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, }, - &ruleRefExpr{ - pos: position{line: 60, col: 9, offset: 1453}, - name: "OperatorColonNode", + }, + &zeroOrOneExpr{ + pos: position{line: 80, col: 38, offset: 1951}, + expr: &actionExpr{ + pos: position{line: 114, col: 5, offset: 2721}, + run: (*parser).callonNode241, + expr: &litMatcher{ + pos: position{line: 114, col: 5, offset: 2721}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, }, }, }, }, - &zeroOrOneExpr{ - pos: position{line: 61, col: 7, offset: 1477}, - expr: &litMatcher{ - pos: position{line: 61, col: 7, offset: 1477}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", - }, - }, - &labeledExpr{ - pos: position{line: 61, col: 12, offset: 1482}, - label: "v", - expr: &choiceExpr{ - pos: position{line: 62, col: 9, offset: 1494}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 62, col: 9, offset: 1494}, - name: "DateTime", + }, + &actionExpr{ + pos: position{line: 85, col: 6, offset: 2049}, + run: (*parser).callonNode243, + expr: &seqExpr{ + pos: position{line: 85, col: 6, offset: 2049}, + exprs: []any{ + &zeroOrOneExpr{ + pos: position{line: 85, col: 6, offset: 2049}, + expr: &actionExpr{ + pos: position{line: 114, col: 5, offset: 2721}, + run: (*parser).callonNode246, + expr: &litMatcher{ + pos: position{line: 114, col: 5, offset: 2721}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, }, - &ruleRefExpr{ - pos: position{line: 63, col: 9, offset: 1513}, - name: "FullDate", + }, + &actionExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + run: (*parser).callonNode248, + expr: &zeroOrMoreExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + expr: &charClassMatcher{ + pos: position{line: 213, col: 5, offset: 4364}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, }, - &ruleRefExpr{ - pos: position{line: 64, col: 9, offset: 1532}, - name: "FullTime", + }, + &labeledExpr{ + pos: position{line: 85, col: 27, offset: 2070}, + label: "v", + expr: &oneOrMoreExpr{ + pos: position{line: 85, col: 29, offset: 2072}, + expr: &charClassMatcher{ + pos: position{line: 85, col: 29, offset: 2072}, + val: "[^ :()]", + chars: []rune{' ', ':', '(', ')'}, + ignoreCase: false, + inverted: true, + }, + }, + }, + &actionExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + run: (*parser).callonNode254, + expr: &zeroOrMoreExpr{ + pos: position{line: 213, col: 5, offset: 4364}, + expr: &charClassMatcher{ + pos: position{line: 213, col: 5, offset: 4364}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 85, col: 40, offset: 2083}, + expr: &actionExpr{ + pos: position{line: 114, col: 5, offset: 2721}, + run: (*parser).callonNode258, + expr: &litMatcher{ + pos: position{line: 114, col: 5, offset: 2721}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, }, }, - }, - }, - &zeroOrOneExpr{ - pos: position{line: 65, col: 7, offset: 1547}, - expr: &litMatcher{ - pos: position{line: 65, col: 7, offset: 1547}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", }, }, }, @@ -344,1053 +1364,1144 @@ var g = &grammar{ }, }, { - name: "TextPropertyRestrictionNode", - pos: position{line: 69, col: 1, offset: 1618}, + name: "GroupNode", + pos: position{line: 31, col: 1, offset: 595}, expr: &actionExpr{ - pos: position{line: 70, col: 5, offset: 1653}, - run: (*parser).callonTextPropertyRestrictionNode1, + pos: position{line: 32, col: 5, offset: 612}, + run: (*parser).callonGroupNode1, expr: &seqExpr{ - pos: position{line: 70, col: 5, offset: 1653}, + pos: position{line: 32, col: 5, offset: 612}, exprs: []any{ &labeledExpr{ - pos: position{line: 70, col: 5, offset: 1653}, + pos: position{line: 32, col: 5, offset: 612}, label: "k", - expr: &oneOrMoreExpr{ - pos: position{line: 70, col: 7, offset: 1655}, - expr: &ruleRefExpr{ - pos: position{line: 70, col: 7, offset: 1655}, - name: "Char", + expr: &zeroOrOneExpr{ + pos: position{line: 32, col: 7, offset: 614}, + expr: &oneOrMoreExpr{ + pos: position{line: 32, col: 8, offset: 615}, + expr: &actionExpr{ + pos: position{line: 198, col: 5, offset: 4194}, + run: (*parser).callonGroupNode6, + expr: &charClassMatcher{ + pos: position{line: 198, col: 5, offset: 4194}, + val: "[A-Za-z]", + ranges: []rune{'A', 'Z', 'a', 'z'}, + ignoreCase: false, + inverted: false, + }, + }, }, }, }, - &choiceExpr{ - pos: position{line: 70, col: 14, offset: 1662}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 70, col: 14, offset: 1662}, - name: "OperatorColonNode", - }, - &ruleRefExpr{ - pos: position{line: 70, col: 34, offset: 1682}, - name: "OperatorEqualNode", - }, - }, - }, - &labeledExpr{ - pos: position{line: 70, col: 53, offset: 1701}, - label: "v", + &zeroOrOneExpr{ + pos: position{line: 32, col: 16, offset: 623}, expr: &choiceExpr{ - pos: position{line: 70, col: 56, offset: 1704}, + pos: position{line: 32, col: 17, offset: 624}, alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 70, col: 56, offset: 1704}, - name: "String", + &actionExpr{ + pos: position{line: 114, col: 5, offset: 2721}, + run: (*parser).callonGroupNode10, + expr: &litMatcher{ + pos: position{line: 114, col: 5, offset: 2721}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, }, - &oneOrMoreExpr{ - pos: position{line: 70, col: 65, offset: 1713}, - expr: &charClassMatcher{ - pos: position{line: 70, col: 65, offset: 1713}, - val: "[^ ()]", - chars: []rune{' ', '(', ')'}, + &actionExpr{ + pos: position{line: 119, col: 5, offset: 2807}, + run: (*parser).callonGroupNode12, + expr: &litMatcher{ + pos: position{line: 119, col: 5, offset: 2807}, + val: "=", ignoreCase: false, - inverted: true, + want: "\"=\"", }, }, }, }, }, - }, - }, - }, - }, - { - name: "FreeTextKeywordNodes", - pos: position{line: 78, col: 1, offset: 1919}, - expr: &choiceExpr{ - pos: position{line: 79, col: 5, offset: 1947}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 79, col: 5, offset: 1947}, - name: "PhraseNode", - }, - &ruleRefExpr{ - pos: position{line: 80, col: 5, offset: 1964}, - name: "WordNode", - }, - }, - }, - }, - { - name: "PhraseNode", - pos: position{line: 82, col: 1, offset: 1974}, - expr: &actionExpr{ - pos: position{line: 83, col: 6, offset: 1993}, - run: (*parser).callonPhraseNode1, - expr: &seqExpr{ - pos: position{line: 83, col: 6, offset: 1993}, - exprs: []any{ - &zeroOrOneExpr{ - pos: position{line: 83, col: 6, offset: 1993}, - expr: &ruleRefExpr{ - pos: position{line: 83, col: 6, offset: 1993}, - name: "OperatorColonNode", - }, - }, - &ruleRefExpr{ - pos: position{line: 83, col: 25, offset: 2012}, - name: "_", - }, - &labeledExpr{ - pos: position{line: 83, col: 27, offset: 2014}, - label: "v", - expr: &ruleRefExpr{ - pos: position{line: 83, col: 29, offset: 2016}, - name: "String", - }, - }, - &ruleRefExpr{ - pos: position{line: 83, col: 36, offset: 2023}, - name: "_", - }, - &zeroOrOneExpr{ - pos: position{line: 83, col: 38, offset: 2025}, - expr: &ruleRefExpr{ - pos: position{line: 83, col: 38, offset: 2025}, - name: "OperatorColonNode", - }, - }, - }, - }, - }, - }, - { - name: "WordNode", - pos: position{line: 87, col: 1, offset: 2106}, - expr: &actionExpr{ - pos: position{line: 88, col: 6, offset: 2123}, - run: (*parser).callonWordNode1, - expr: &seqExpr{ - pos: position{line: 88, col: 6, offset: 2123}, - exprs: []any{ - &zeroOrOneExpr{ - pos: position{line: 88, col: 6, offset: 2123}, - expr: &ruleRefExpr{ - pos: position{line: 88, col: 6, offset: 2123}, - name: "OperatorColonNode", - }, - }, - &ruleRefExpr{ - pos: position{line: 88, col: 25, offset: 2142}, - name: "_", + &litMatcher{ + pos: position{line: 32, col: 57, offset: 664}, + val: "(", + ignoreCase: false, + want: "\"(\"", }, &labeledExpr{ - pos: position{line: 88, col: 27, offset: 2144}, + pos: position{line: 32, col: 61, offset: 668}, label: "v", - expr: &oneOrMoreExpr{ - pos: position{line: 88, col: 29, offset: 2146}, - expr: &charClassMatcher{ - pos: position{line: 88, col: 29, offset: 2146}, - val: "[^ :()]", - chars: []rune{' ', ':', '(', ')'}, - ignoreCase: false, - inverted: true, - }, - }, - }, - &ruleRefExpr{ - pos: position{line: 88, col: 38, offset: 2155}, - name: "_", - }, - &zeroOrOneExpr{ - pos: position{line: 88, col: 40, offset: 2157}, expr: &ruleRefExpr{ - pos: position{line: 88, col: 40, offset: 2157}, - name: "OperatorColonNode", + pos: position{line: 32, col: 63, offset: 670}, + name: "Nodes", }, }, - }, - }, - }, - }, - { - name: "OperatorBooleanNodes", - pos: position{line: 96, col: 1, offset: 2366}, - expr: &choiceExpr{ - pos: position{line: 97, col: 5, offset: 2394}, - alternatives: []any{ - &ruleRefExpr{ - pos: position{line: 97, col: 5, offset: 2394}, - name: "OperatorBooleanAndNode", - }, - &ruleRefExpr{ - pos: position{line: 98, col: 5, offset: 2423}, - name: "OperatorBooleanNotNode", - }, - &ruleRefExpr{ - pos: position{line: 99, col: 5, offset: 2452}, - name: "OperatorBooleanOrNode", - }, - }, - }, - }, - { - name: "OperatorBooleanAndNode", - pos: position{line: 101, col: 1, offset: 2475}, - expr: &actionExpr{ - pos: position{line: 102, col: 5, offset: 2505}, - run: (*parser).callonOperatorBooleanAndNode1, - expr: &choiceExpr{ - pos: position{line: 102, col: 6, offset: 2506}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 102, col: 6, offset: 2506}, - val: "AND", - ignoreCase: false, - want: "\"AND\"", - }, - &litMatcher{ - pos: position{line: 102, col: 14, offset: 2514}, - val: "+", - ignoreCase: false, - want: "\"+\"", - }, - }, - }, - }, - }, - { - name: "OperatorBooleanNotNode", - pos: position{line: 106, col: 1, offset: 2576}, - expr: &actionExpr{ - pos: position{line: 107, col: 5, offset: 2606}, - run: (*parser).callonOperatorBooleanNotNode1, - expr: &choiceExpr{ - pos: position{line: 107, col: 6, offset: 2607}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 107, col: 6, offset: 2607}, - val: "NOT", - ignoreCase: false, - want: "\"NOT\"", - }, - &litMatcher{ - pos: position{line: 107, col: 14, offset: 2615}, - val: "-", - ignoreCase: false, - want: "\"-\"", - }, - }, - }, - }, - }, - { - name: "OperatorBooleanOrNode", - pos: position{line: 111, col: 1, offset: 2677}, - expr: &actionExpr{ - pos: position{line: 112, col: 5, offset: 2706}, - run: (*parser).callonOperatorBooleanOrNode1, - expr: &litMatcher{ - pos: position{line: 112, col: 6, offset: 2707}, - val: "OR", - ignoreCase: false, - want: "\"OR\"", - }, - }, - }, - { - name: "OperatorColonNode", - pos: position{line: 116, col: 1, offset: 2770}, - expr: &actionExpr{ - pos: position{line: 117, col: 5, offset: 2795}, - run: (*parser).callonOperatorColonNode1, - expr: &litMatcher{ - pos: position{line: 117, col: 5, offset: 2795}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - }, - }, - { - name: "OperatorEqualNode", - pos: position{line: 121, col: 1, offset: 2856}, - expr: &actionExpr{ - pos: position{line: 122, col: 5, offset: 2881}, - run: (*parser).callonOperatorEqualNode1, - expr: &litMatcher{ - pos: position{line: 122, col: 5, offset: 2881}, - val: "=", - ignoreCase: false, - want: "\"=\"", - }, - }, - }, - { - name: "OperatorLessNode", - pos: position{line: 126, col: 1, offset: 2942}, - expr: &actionExpr{ - pos: position{line: 127, col: 5, offset: 2966}, - run: (*parser).callonOperatorLessNode1, - expr: &litMatcher{ - pos: position{line: 127, col: 5, offset: 2966}, - val: "<", - ignoreCase: false, - want: "\"<\"", - }, - }, - }, - { - name: "OperatorLessOrEqualNode", - pos: position{line: 131, col: 1, offset: 3027}, - expr: &actionExpr{ - pos: position{line: 132, col: 5, offset: 3058}, - run: (*parser).callonOperatorLessOrEqualNode1, - expr: &litMatcher{ - pos: position{line: 132, col: 5, offset: 3058}, - val: "<=", - ignoreCase: false, - want: "\"<=\"", - }, - }, - }, - { - name: "OperatorGreaterNode", - pos: position{line: 136, col: 1, offset: 3120}, - expr: &actionExpr{ - pos: position{line: 137, col: 5, offset: 3147}, - run: (*parser).callonOperatorGreaterNode1, - expr: &litMatcher{ - pos: position{line: 137, col: 5, offset: 3147}, - val: ">", - ignoreCase: false, - want: "\">\"", - }, - }, - }, - { - name: "OperatorGreaterOrEqualNode", - pos: position{line: 141, col: 1, offset: 3208}, - expr: &actionExpr{ - pos: position{line: 142, col: 5, offset: 3242}, - run: (*parser).callonOperatorGreaterOrEqualNode1, - expr: &litMatcher{ - pos: position{line: 142, col: 5, offset: 3242}, - val: ">=", - ignoreCase: false, - want: "\">=\"", - }, - }, - }, - { - name: "TimeYear", - pos: position{line: 151, col: 1, offset: 3428}, - expr: &actionExpr{ - pos: position{line: 152, col: 5, offset: 3444}, - run: (*parser).callonTimeYear1, - expr: &seqExpr{ - pos: position{line: 152, col: 5, offset: 3444}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 152, col: 5, offset: 3444}, - name: "Digit", - }, - &ruleRefExpr{ - pos: position{line: 152, col: 11, offset: 3450}, - name: "Digit", - }, - &ruleRefExpr{ - pos: position{line: 152, col: 17, offset: 3456}, - name: "Digit", - }, - &ruleRefExpr{ - pos: position{line: 152, col: 23, offset: 3462}, - name: "Digit", - }, - }, - }, - }, - }, - { - name: "TimeMonth", - pos: position{line: 156, col: 1, offset: 3504}, - expr: &actionExpr{ - pos: position{line: 157, col: 5, offset: 3521}, - run: (*parser).callonTimeMonth1, - expr: &seqExpr{ - pos: position{line: 157, col: 5, offset: 3521}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 157, col: 5, offset: 3521}, - name: "Digit", - }, - &ruleRefExpr{ - pos: position{line: 157, col: 11, offset: 3527}, - name: "Digit", - }, - }, - }, - }, - }, - { - name: "TimeDay", - pos: position{line: 161, col: 1, offset: 3569}, - expr: &actionExpr{ - pos: position{line: 162, col: 5, offset: 3584}, - run: (*parser).callonTimeDay1, - expr: &seqExpr{ - pos: position{line: 162, col: 5, offset: 3584}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 162, col: 5, offset: 3584}, - name: "Digit", - }, - &ruleRefExpr{ - pos: position{line: 162, col: 11, offset: 3590}, - name: "Digit", - }, - }, - }, - }, - }, - { - name: "TimeHour", - pos: position{line: 166, col: 1, offset: 3632}, - expr: &actionExpr{ - pos: position{line: 167, col: 5, offset: 3648}, - run: (*parser).callonTimeHour1, - expr: &seqExpr{ - pos: position{line: 167, col: 5, offset: 3648}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 167, col: 5, offset: 3648}, - name: "Digit", - }, - &ruleRefExpr{ - pos: position{line: 167, col: 11, offset: 3654}, - name: "Digit", - }, - }, - }, - }, - }, - { - name: "TimeMinute", - pos: position{line: 171, col: 1, offset: 3696}, - expr: &actionExpr{ - pos: position{line: 172, col: 5, offset: 3714}, - run: (*parser).callonTimeMinute1, - expr: &seqExpr{ - pos: position{line: 172, col: 5, offset: 3714}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 172, col: 5, offset: 3714}, - name: "Digit", - }, - &ruleRefExpr{ - pos: position{line: 172, col: 11, offset: 3720}, - name: "Digit", - }, - }, - }, - }, - }, - { - name: "TimeSecond", - pos: position{line: 176, col: 1, offset: 3762}, - expr: &actionExpr{ - pos: position{line: 177, col: 5, offset: 3780}, - run: (*parser).callonTimeSecond1, - expr: &seqExpr{ - pos: position{line: 177, col: 5, offset: 3780}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 177, col: 5, offset: 3780}, - name: "Digit", - }, - &ruleRefExpr{ - pos: position{line: 177, col: 11, offset: 3786}, - name: "Digit", - }, - }, - }, - }, - }, - { - name: "FullDate", - pos: position{line: 181, col: 1, offset: 3828}, - expr: &actionExpr{ - pos: position{line: 182, col: 5, offset: 3844}, - run: (*parser).callonFullDate1, - expr: &seqExpr{ - pos: position{line: 182, col: 5, offset: 3844}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 182, col: 5, offset: 3844}, - name: "TimeYear", - }, &litMatcher{ - pos: position{line: 182, col: 14, offset: 3853}, - val: "-", - ignoreCase: false, - want: "\"-\"", - }, - &ruleRefExpr{ - pos: position{line: 182, col: 18, offset: 3857}, - name: "TimeMonth", - }, - &litMatcher{ - pos: position{line: 182, col: 28, offset: 3867}, - val: "-", + pos: position{line: 32, col: 69, offset: 676}, + val: ")", ignoreCase: false, - want: "\"-\"", - }, - &ruleRefExpr{ - pos: position{line: 182, col: 32, offset: 3871}, - name: "TimeDay", + want: "\")\"", }, }, }, }, }, - { - name: "FullTime", - pos: position{line: 186, col: 1, offset: 3915}, - expr: &actionExpr{ - pos: position{line: 187, col: 5, offset: 3931}, - run: (*parser).callonFullTime1, - expr: &seqExpr{ - pos: position{line: 187, col: 5, offset: 3931}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 187, col: 5, offset: 3931}, - name: "TimeHour", - }, - &litMatcher{ - pos: position{line: 187, col: 14, offset: 3940}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - &ruleRefExpr{ - pos: position{line: 187, col: 18, offset: 3944}, - name: "TimeMinute", - }, - &litMatcher{ - pos: position{line: 187, col: 29, offset: 3955}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - &ruleRefExpr{ - pos: position{line: 187, col: 33, offset: 3959}, - name: "TimeSecond", - }, - &zeroOrOneExpr{ - pos: position{line: 187, col: 44, offset: 3970}, - expr: &seqExpr{ - pos: position{line: 187, col: 45, offset: 3971}, - exprs: []any{ - &litMatcher{ - pos: position{line: 187, col: 45, offset: 3971}, - val: ".", - ignoreCase: false, - want: "\".\"", - }, - &oneOrMoreExpr{ - pos: position{line: 187, col: 49, offset: 3975}, - expr: &ruleRefExpr{ - pos: position{line: 187, col: 49, offset: 3975}, - name: "Digit", - }, - }, - }, - }, - }, - &choiceExpr{ - pos: position{line: 187, col: 59, offset: 3985}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 187, col: 59, offset: 3985}, - val: "Z", - ignoreCase: false, - want: "\"Z\"", - }, - &seqExpr{ - pos: position{line: 187, col: 65, offset: 3991}, - exprs: []any{ - &choiceExpr{ - pos: position{line: 187, col: 66, offset: 3992}, - alternatives: []any{ - &litMatcher{ - pos: position{line: 187, col: 66, offset: 3992}, - val: "+", - ignoreCase: false, - want: "\"+\"", - }, - &litMatcher{ - pos: position{line: 187, col: 72, offset: 3998}, - val: "-", - ignoreCase: false, - want: "\"-\"", - }, - }, - }, - &ruleRefExpr{ - pos: position{line: 187, col: 77, offset: 4003}, - name: "TimeHour", - }, - &litMatcher{ - pos: position{line: 187, col: 86, offset: 4012}, - val: ":", - ignoreCase: false, - want: "\":\"", - }, - &ruleRefExpr{ - pos: position{line: 187, col: 90, offset: 4016}, - name: "TimeMinute", - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "DateTime", - pos: position{line: 191, col: 1, offset: 4064}, - expr: &actionExpr{ - pos: position{line: 192, col: 5, offset: 4077}, - run: (*parser).callonDateTime1, - expr: &seqExpr{ - pos: position{line: 192, col: 5, offset: 4077}, - exprs: []any{ - &ruleRefExpr{ - pos: position{line: 192, col: 5, offset: 4077}, - name: "FullDate", - }, - &litMatcher{ - pos: position{line: 192, col: 14, offset: 4086}, - val: "T", - ignoreCase: false, - want: "\"T\"", - }, - &ruleRefExpr{ - pos: position{line: 192, col: 18, offset: 4090}, - name: "FullTime", - }, - }, - }, - }, - }, - { - name: "Char", - pos: position{line: 200, col: 1, offset: 4256}, - expr: &actionExpr{ - pos: position{line: 201, col: 5, offset: 4268}, - run: (*parser).callonChar1, - expr: &charClassMatcher{ - pos: position{line: 201, col: 5, offset: 4268}, - val: "[A-Za-z]", - ranges: []rune{'A', 'Z', 'a', 'z'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - { - name: "String", - pos: position{line: 205, col: 1, offset: 4313}, - expr: &actionExpr{ - pos: position{line: 206, col: 5, offset: 4327}, - run: (*parser).callonString1, - expr: &seqExpr{ - pos: position{line: 206, col: 5, offset: 4327}, - exprs: []any{ - &litMatcher{ - pos: position{line: 206, col: 5, offset: 4327}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", - }, - &labeledExpr{ - pos: position{line: 206, col: 9, offset: 4331}, - label: "v", - expr: &zeroOrMoreExpr{ - pos: position{line: 206, col: 11, offset: 4333}, - expr: &charClassMatcher{ - pos: position{line: 206, col: 11, offset: 4333}, - val: "[^\"]", - chars: []rune{'"'}, - ignoreCase: false, - inverted: true, - }, - }, - }, - &litMatcher{ - pos: position{line: 206, col: 17, offset: 4339}, - val: "\"", - ignoreCase: false, - want: "\"\\\"\"", - }, - }, - }, - }, - }, - { - name: "Digit", - pos: position{line: 210, col: 1, offset: 4374}, - expr: &actionExpr{ - pos: position{line: 211, col: 5, offset: 4387}, - run: (*parser).callonDigit1, - expr: &charClassMatcher{ - pos: position{line: 211, col: 5, offset: 4387}, - val: "[0-9]", - ranges: []rune{'0', '9'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - { - name: "_", - pos: position{line: 215, col: 1, offset: 4429}, - expr: &actionExpr{ - pos: position{line: 216, col: 5, offset: 4438}, - run: (*parser).callon_1, - expr: &zeroOrMoreExpr{ - pos: position{line: 216, col: 5, offset: 4438}, - expr: &charClassMatcher{ - pos: position{line: 216, col: 5, offset: 4438}, - val: "[ \\t]", - chars: []rune{' ', '\t'}, - ignoreCase: false, - inverted: false, - }, - }, - }, - }, }, } -func (c *current) onAST1(n any) (any, error) { - return buildAST(n, c.text, c.pos) +func (c *current) onAST1(n any) (any, error) { + return buildAST(n, c.text, c.pos) + +} + +func (p *parser) callonAST1() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onAST1(stack["n"]) +} + +func (c *current) onNodes3() (any, error) { + return nil, nil + +} + +func (p *parser) callonNodes3() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNodes3() +} + +func (c *current) onNode7() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode7() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode7() +} + +func (c *current) onNode10() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode10() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode10() +} + +func (c *current) onNode12() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode12() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode12() +} + +func (c *current) onNode3(k, v any) (any, error) { + return buildBooleanNode(k, v, c.text, c.pos) + +} + +func (p *parser) callonNode3() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode3(stack["k"], stack["v"]) +} + +func (c *current) onNode22() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode22() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode22() +} + +func (c *current) onNode26() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode26() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode26() +} + +func (c *current) onNode28() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode28() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode28() +} + +func (c *current) onNode30() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode30() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode30() +} + +func (c *current) onNode32() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode32() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode32() +} + +func (c *current) onNode34() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode34() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode34() +} + +func (c *current) onNode36() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode36() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode36() +} + +func (c *current) onNode48() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode48() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode48() +} + +func (c *current) onNode50() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode50() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode50() +} + +func (c *current) onNode52() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode52() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode52() +} + +func (c *current) onNode54() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode54() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode54() +} + +func (c *current) onNode46() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode46() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode46() +} + +func (c *current) onNode59() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode59() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode59() +} + +func (c *current) onNode61() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode61() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode61() +} + +func (c *current) onNode57() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode57() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode57() +} + +func (c *current) onNode66() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode66() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode66() +} + +func (c *current) onNode68() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode68() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode68() +} + +func (c *current) onNode64() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode64() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode64() +} + +func (c *current) onNode44() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode44() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode44() +} + +func (c *current) onNode75() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode75() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode75() +} + +func (c *current) onNode77() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode77() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode77() +} + +func (c *current) onNode73() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode73() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode73() +} + +func (c *current) onNode82() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode82() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode82() +} + +func (c *current) onNode84() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode84() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode84() +} + +func (c *current) onNode80() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode80() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode80() +} + +func (c *current) onNode89() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode89() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode89() +} + +func (c *current) onNode91() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode91() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode91() +} + +func (c *current) onNode87() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode87() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode87() +} + +func (c *current) onNode97() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode97() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode97() +} + +func (c *current) onNode105() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode105() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode105() +} + +func (c *current) onNode107() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode107() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode107() +} + +func (c *current) onNode103() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode103() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode103() +} + +func (c *current) onNode112() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode112() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode112() +} + +func (c *current) onNode114() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode114() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode114() +} + +func (c *current) onNode110() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode110() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode110() +} + +func (c *current) onNode71() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode71() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode71() +} + +func (c *current) onNode42() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode42() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode42() +} + +func (c *current) onNode120() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode120() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode120() +} + +func (c *current) onNode122() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode122() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode122() +} + +func (c *current) onNode124() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode124() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode124() +} + +func (c *current) onNode126() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode126() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode126() +} + +func (c *current) onNode118() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode118() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode118() +} + +func (c *current) onNode131() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode131() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode131() +} + +func (c *current) onNode133() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode133() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode133() +} + +func (c *current) onNode129() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode129() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode129() +} + +func (c *current) onNode138() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode138() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode138() +} + +func (c *current) onNode140() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode140() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode140() +} + +func (c *current) onNode136() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode136() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode136() +} + +func (c *current) onNode116() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode116() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode116() +} + +func (c *current) onNode146() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode146() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode146() +} + +func (c *current) onNode148() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode148() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode148() +} + +func (c *current) onNode144() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode144() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode144() +} + +func (c *current) onNode153() (any, error) { + return c.text, nil } -func (p *parser) callonAST1() (any, error) { +func (p *parser) callonNode153() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onAST1(stack["n"]) + return p.cur.onNode153() } -func (c *current) onGroupNode1(k, v any) (any, error) { - return buildGroupNode(k, v, c.text, c.pos) +func (c *current) onNode155() (any, error) { + return c.text, nil } -func (p *parser) callonGroupNode1() (any, error) { +func (p *parser) callonNode155() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onGroupNode1(stack["k"], stack["v"]) + return p.cur.onNode155() } -func (c *current) onYesNoPropertyRestrictionNode1(k, v any) (any, error) { - return buildBooleanNode(k, v, c.text, c.pos) +func (c *current) onNode151() (any, error) { + return c.text, nil } -func (p *parser) callonYesNoPropertyRestrictionNode1() (any, error) { +func (p *parser) callonNode151() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onYesNoPropertyRestrictionNode1(stack["k"], stack["v"]) + return p.cur.onNode151() } -func (c *current) onDateTimeRestrictionNode1(k, o, v any) (any, error) { - return buildDateTimeNode(k, o, v, c.text, c.pos) +func (c *current) onNode160() (any, error) { + return c.text, nil } -func (p *parser) callonDateTimeRestrictionNode1() (any, error) { +func (p *parser) callonNode160() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onDateTimeRestrictionNode1(stack["k"], stack["o"], stack["v"]) + return p.cur.onNode160() } -func (c *current) onTextPropertyRestrictionNode1(k, v any) (any, error) { - return buildStringNode(k, v, c.text, c.pos) +func (c *current) onNode162() (any, error) { + return c.text, nil } -func (p *parser) callonTextPropertyRestrictionNode1() (any, error) { +func (p *parser) callonNode162() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onTextPropertyRestrictionNode1(stack["k"], stack["v"]) + return p.cur.onNode162() } -func (c *current) onPhraseNode1(v any) (any, error) { - return buildStringNode("", v, c.text, c.pos) +func (c *current) onNode158() (any, error) { + return c.text, nil } -func (p *parser) callonPhraseNode1() (any, error) { +func (p *parser) callonNode158() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onPhraseNode1(stack["v"]) + return p.cur.onNode158() } -func (c *current) onWordNode1(v any) (any, error) { - return buildStringNode("", v, c.text, c.pos) +func (c *current) onNode168() (any, error) { + return c.text, nil } -func (p *parser) callonWordNode1() (any, error) { +func (p *parser) callonNode168() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onWordNode1(stack["v"]) + return p.cur.onNode168() } -func (c *current) onOperatorBooleanAndNode1() (any, error) { - return buildOperatorNode(c.text, c.pos) +func (c *current) onNode176() (any, error) { + return c.text, nil } -func (p *parser) callonOperatorBooleanAndNode1() (any, error) { +func (p *parser) callonNode176() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorBooleanAndNode1() + return p.cur.onNode176() } -func (c *current) onOperatorBooleanNotNode1() (any, error) { - return buildOperatorNode(c.text, c.pos) +func (c *current) onNode178() (any, error) { + return c.text, nil } -func (p *parser) callonOperatorBooleanNotNode1() (any, error) { +func (p *parser) callonNode178() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorBooleanNotNode1() + return p.cur.onNode178() } -func (c *current) onOperatorBooleanOrNode1() (any, error) { - return buildOperatorNode(c.text, c.pos) +func (c *current) onNode174() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode174() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode174() +} + +func (c *current) onNode183() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode183() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode183() +} + +func (c *current) onNode185() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode185() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode185() +} + +func (c *current) onNode181() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode181() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode181() +} + +func (c *current) onNode142() (any, error) { + return c.text, nil } -func (p *parser) callonOperatorBooleanOrNode1() (any, error) { +func (p *parser) callonNode142() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorBooleanOrNode1() + return p.cur.onNode142() } -func (c *current) onOperatorColonNode1() (any, error) { +func (c *current) onNode18(k, o, v any) (any, error) { + return buildDateTimeNode(k, o, v, c.text, c.pos) + +} + +func (p *parser) callonNode18() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode18(stack["k"], stack["o"], stack["v"]) +} + +func (c *current) onNode193() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode193() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode193() +} + +func (c *current) onNode196() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonOperatorColonNode1() (any, error) { +func (p *parser) callonNode196() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorColonNode1() + return p.cur.onNode196() } -func (c *current) onOperatorEqualNode1() (any, error) { +func (c *current) onNode198() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonOperatorEqualNode1() (any, error) { +func (p *parser) callonNode198() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode198() +} + +func (c *current) onNode202(v any) (any, error) { + return v, nil + +} + +func (p *parser) callonNode202() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode202(stack["v"]) +} + +func (c *current) onNode189(k, v any) (any, error) { + return buildStringNode(k, v, c.text, c.pos) + +} + +func (p *parser) callonNode189() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorEqualNode1() + return p.cur.onNode189(stack["k"], stack["v"]) } -func (c *current) onOperatorLessNode1() (any, error) { +func (c *current) onNode211() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonOperatorLessNode1() (any, error) { +func (p *parser) callonNode211() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorLessNode1() + return p.cur.onNode211() } -func (c *current) onOperatorLessOrEqualNode1() (any, error) { +func (c *current) onNode215() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonOperatorLessOrEqualNode1() (any, error) { +func (p *parser) callonNode215() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorLessOrEqualNode1() + return p.cur.onNode215() } -func (c *current) onOperatorGreaterNode1() (any, error) { +func (c *current) onNode219() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonOperatorGreaterNode1() (any, error) { +func (p *parser) callonNode219() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorGreaterNode1() + return p.cur.onNode219() } -func (c *current) onOperatorGreaterOrEqualNode1() (any, error) { +func (c *current) onNode224() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonOperatorGreaterOrEqualNode1() (any, error) { +func (p *parser) callonNode224() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onOperatorGreaterOrEqualNode1() + return p.cur.onNode224() } -func (c *current) onTimeYear1() (any, error) { - return c.text, nil +func (c *current) onNode226() (any, error) { + return nil, nil } -func (p *parser) callonTimeYear1() (any, error) { +func (p *parser) callonNode226() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onTimeYear1() + return p.cur.onNode226() } -func (c *current) onTimeMonth1() (any, error) { - return c.text, nil +func (c *current) onNode230(v any) (any, error) { + return v, nil } -func (p *parser) callonTimeMonth1() (any, error) { +func (p *parser) callonNode230() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onTimeMonth1() + return p.cur.onNode230(stack["v"]) } -func (c *current) onTimeDay1() (any, error) { - return c.text, nil +func (c *current) onNode237(v any) (any, error) { + return nil, nil } -func (p *parser) callonTimeDay1() (any, error) { +func (p *parser) callonNode237() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onTimeDay1() + return p.cur.onNode237(stack["v"]) } -func (c *current) onTimeHour1() (any, error) { - return c.text, nil +func (c *current) onNode241() (any, error) { + return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonTimeHour1() (any, error) { +func (p *parser) callonNode241() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onTimeHour1() + return p.cur.onNode241() } -func (c *current) onTimeMinute1() (any, error) { - return c.text, nil +func (c *current) onNode221(v any) (any, error) { + return buildStringNode("", v, c.text, c.pos) } -func (p *parser) callonTimeMinute1() (any, error) { +func (p *parser) callonNode221() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onTimeMinute1() + return p.cur.onNode221(stack["v"]) } -func (c *current) onTimeSecond1() (any, error) { - return c.text, nil +func (c *current) onNode246() (any, error) { + return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonTimeSecond1() (any, error) { +func (p *parser) callonNode246() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onTimeSecond1() + return p.cur.onNode246() } -func (c *current) onFullDate1() (any, error) { - return c.text, nil +func (c *current) onNode248() (any, error) { + return nil, nil } -func (p *parser) callonFullDate1() (any, error) { +func (p *parser) callonNode248() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onFullDate1() + return p.cur.onNode248() } -func (c *current) onFullTime1() (any, error) { - return c.text, nil +func (c *current) onNode254(v any) (any, error) { + return nil, nil } -func (p *parser) callonFullTime1() (any, error) { +func (p *parser) callonNode254() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onFullTime1() + return p.cur.onNode254(stack["v"]) } -func (c *current) onDateTime1() (any, error) { - return c.text, nil +func (c *current) onNode258() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode258() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode258() +} + +func (c *current) onNode243(v any) (any, error) { + return buildStringNode("", v, c.text, c.pos) } -func (p *parser) callonDateTime1() (any, error) { +func (p *parser) callonNode243() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onDateTime1() + return p.cur.onNode243(stack["v"]) } -func (c *current) onChar1() (any, error) { +func (c *current) onGroupNode6() (any, error) { return c.text, nil } -func (p *parser) callonChar1() (any, error) { +func (p *parser) callonGroupNode6() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onChar1() + return p.cur.onGroupNode6() } -func (c *current) onString1(v any) (any, error) { - return v, nil +func (c *current) onGroupNode10() (any, error) { + return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonString1() (any, error) { +func (p *parser) callonGroupNode10() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onString1(stack["v"]) + return p.cur.onGroupNode10() } -func (c *current) onDigit1() (any, error) { - return c.text, nil +func (c *current) onGroupNode12() (any, error) { + return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonDigit1() (any, error) { +func (p *parser) callonGroupNode12() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onDigit1() + return p.cur.onGroupNode12() } -func (c *current) on_1() (any, error) { - return nil, nil +func (c *current) onGroupNode1(k, v any) (any, error) { + return buildGroupNode(k, v, c.text, c.pos) } -func (p *parser) callon_1() (any, error) { +func (p *parser) callonGroupNode1() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.on_1() + return p.cur.onGroupNode1(stack["k"], stack["v"]) } var ( @@ -1445,62 +2556,6 @@ func Entrypoint(ruleName string) Option { } } -// Statistics adds a user provided Stats struct to the parser to allow -// the user to process the results after the parsing has finished. -// Also the key for the "no match" counter is set. -// -// Example usage: -// -// input := "input" -// stats := Stats{} -// _, err := Parse("input-file", []byte(input), Statistics(&stats, "no match")) -// if err != nil { -// log.Panicln(err) -// } -// b, err := json.MarshalIndent(stats.ChoiceAltCnt, "", " ") -// if err != nil { -// log.Panicln(err) -// } -// fmt.Println(string(b)) -func Statistics(stats *Stats, choiceNoMatch string) Option { - return func(p *parser) Option { - oldStats := p.Stats - p.Stats = stats - oldChoiceNoMatch := p.choiceNoMatch - p.choiceNoMatch = choiceNoMatch - if p.Stats.ChoiceAltCnt == nil { - p.Stats.ChoiceAltCnt = make(map[string]map[string]int) - } - return Statistics(oldStats, oldChoiceNoMatch) - } -} - -// Debug creates an Option to set the debug flag to b. When set to true, -// debugging information is printed to stdout while parsing. -// -// The default is false. -func Debug(b bool) Option { - return func(p *parser) Option { - old := p.debug - p.debug = b - return Debug(old) - } -} - -// Memoize creates an Option to set the memoize flag to b. When set to true, -// the parser will cache all results so each expression is evaluated only -// once. This guarantees linear parsing time even for pathological cases, -// at the expense of more memory and slower times for typical cases. -// -// The default is false. -func Memoize(b bool) Option { - return func(p *parser) Option { - old := p.memoize - p.memoize = b - return Memoize(old) - } -} - // AllowInvalidUTF8 creates an Option to allow invalid UTF-8 bytes. // Every invalid UTF-8 byte is treated as a utf8.RuneError (U+FFFD) // by character class matchers and is matched by the any matcher. @@ -1539,16 +2594,6 @@ func GlobalStore(key string, value any) Option { } } -// InitState creates an Option to set a key to a certain value in -// the global "state" store. -func InitState(key string, value any) Option { - return func(p *parser) Option { - old := p.cur.state[key] - p.cur.state[key] = value - return InitState(key, old) - } -} - // ParseFile parses the file identified by filename. func ParseFile(filename string, opts ...Option) (i any, err error) { f, err := os.Open(filename) @@ -1601,11 +2646,6 @@ type current struct { pos position // start position of the match text []byte // raw text of the match - // state is a store for arbitrary key,value pairs that the user wants to be - // tied to the backtracking of the parser. - // This is always rolled back if a parsing rule fails. - state storeDict - // globalStore is a general store for the user to store arbitrary key-value // pairs that they need to manage and that they do not want tied to the // backtracking of the parser. This is only modified by the user and never @@ -1682,11 +2722,6 @@ type ruleRefExpr struct { name string } -type stateCodeExpr struct { - pos position - run func(*parser) error -} - type andCodeExpr struct { pos position run func(*parser) (bool, error) @@ -1790,7 +2825,6 @@ func newParser(filename string, b []byte, opts ...Option) *parser { pt: savepoint{position: position{line: 1}}, recover: true, cur: current{ - state: make(storeDict), globalStore: make(storeDict), }, maxFailPos: position{col: 1, line: 1}, @@ -1855,12 +2889,6 @@ type parser struct { depth int recover bool - debug bool - - memoize bool - // memoization table for the packrat algorithm: - // map[offset in source] map[expression or rule] {value, match} - memo map[int]map[any]resultTuple // rules table, maps the rule identifier to the rule node rules map[string]*rule @@ -1945,26 +2973,6 @@ func (p *parser) popRecovery() { p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)-1] } -func (p *parser) print(prefix, s string) string { - if !p.debug { - return s - } - - fmt.Printf("%s %d:%d:%d: %s [%#U]\n", - prefix, p.pt.line, p.pt.col, p.pt.offset, s, p.pt.rn) - return s -} - -func (p *parser) in(s string) string { - p.depth++ - return p.print(strings.Repeat(" ", p.depth)+">", s) -} - -func (p *parser) out(s string) string { - p.depth-- - return p.print(strings.Repeat(" ", p.depth)+"<", s) -} - func (p *parser) addErr(err error) { p.addErrAt(err, p.pt.position, []string{}) } @@ -2033,93 +3041,17 @@ func (p *parser) read() { // restore parser position to the savepoint pt. func (p *parser) restore(pt savepoint) { - if p.debug { - defer p.out(p.in("restore")) - } if pt.offset == p.pt.offset { return } p.pt = pt } -// Cloner is implemented by any value that has a Clone method, which returns a -// copy of the value. This is mainly used for types which are not passed by -// value (e.g map, slice, chan) or structs that contain such types. -// -// This is used in conjunction with the global state feature to create proper -// copies of the state to allow the parser to properly restore the state in -// the case of backtracking. -type Cloner interface { - Clone() any -} - -var statePool = &sync.Pool{ - New: func() any { return make(storeDict) }, -} - -func (sd storeDict) Discard() { - for k := range sd { - delete(sd, k) - } - statePool.Put(sd) -} - -// clone and return parser current state. -func (p *parser) cloneState() storeDict { - if p.debug { - defer p.out(p.in("cloneState")) - } - - state := statePool.Get().(storeDict) - for k, v := range p.cur.state { - if c, ok := v.(Cloner); ok { - state[k] = c.Clone() - } else { - state[k] = v - } - } - return state -} - -// restore parser current state to the state storeDict. -// every restoreState should applied only one time for every cloned state -func (p *parser) restoreState(state storeDict) { - if p.debug { - defer p.out(p.in("restoreState")) - } - p.cur.state.Discard() - p.cur.state = state -} - // 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) getMemoized(node any) (resultTuple, bool) { - if len(p.memo) == 0 { - return resultTuple{}, false - } - m := p.memo[p.pt.offset] - if len(m) == 0 { - return resultTuple{}, false - } - res, ok := m[node] - return res, ok -} - -func (p *parser) setMemoized(pt savepoint, node any, tuple resultTuple) { - if p.memo == nil { - p.memo = make(map[int]map[any]resultTuple) - } - m := p.memo[pt.offset] - if m == nil { - m = make(map[any]resultTuple) - p.memo[pt.offset] = m - } - m[node] = tuple -} - func (p *parser) buildRulesTable(g *grammar) { p.rules = make(map[string]*rule, len(g.rules)) for _, r := range g.rules { @@ -2141,9 +3073,6 @@ func (p *parser) parse(g *grammar) (val any, err error) { // and return the panic as an error. defer func() { if e := recover(); e != nil { - if p.debug { - defer p.out(p.in("panic handler")) - } val = nil switch e := e.(type) { case error: @@ -2205,45 +3134,15 @@ func listJoin(list []string, sep string, lastSep string) string { } func (p *parser) parseRule(rule *rule) (any, bool) { - if p.debug { - defer p.out(p.in("parseRule " + rule.name)) - } - - if p.memoize { - res, ok := p.getMemoized(rule) - if ok { - p.restore(res.end) - return res.v, res.b - } - } - - start := p.pt p.rstack = append(p.rstack, rule) p.pushV() val, ok := p.parseExpr(rule.expr) p.popV() p.rstack = p.rstack[:len(p.rstack)-1] - if ok && p.debug { - p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) - } - - if p.memoize { - p.setMemoized(start, rule, resultTuple{val, ok, p.pt}) - } return val, ok } func (p *parser) parseExpr(expr any) (any, bool) { - var pt savepoint - - if p.memoize { - res, ok := p.getMemoized(expr) - if ok { - p.restore(res.end) - return res.v, res.b - } - pt = p.pt - } p.ExprCnt++ if p.ExprCnt > p.maxExprCnt { @@ -2281,8 +3180,6 @@ func (p *parser) parseExpr(expr any) (any, bool) { val, ok = p.parseRuleRefExpr(expr) case *seqExpr: val, ok = p.parseSeqExpr(expr) - case *stateCodeExpr: - val, ok = p.parseStateCodeExpr(expr) case *throwExpr: val, ok = p.parseThrowExpr(expr) case *zeroOrMoreExpr: @@ -2292,74 +3189,46 @@ func (p *parser) parseExpr(expr any) (any, bool) { default: panic(fmt.Sprintf("unknown expression type %T", expr)) } - if p.memoize { - p.setMemoized(pt, expr, resultTuple{val, ok, p.pt}) - } return val, ok } func (p *parser) parseActionExpr(act *actionExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseActionExpr")) - } - start := p.pt val, ok := p.parseExpr(act.expr) if ok { p.cur.pos = start.position p.cur.text = p.sliceFrom(start) - state := p.cloneState() actVal, err := act.run(p) if err != nil { p.addErrAt(err, start.position, []string{}) } - p.restoreState(state) val = actVal } - if ok && p.debug { - p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) - } return val, ok } func (p *parser) parseAndCodeExpr(and *andCodeExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseAndCodeExpr")) - } - - state := p.cloneState() ok, err := and.run(p) if err != nil { p.addErr(err) } - p.restoreState(state) return nil, ok } func (p *parser) parseAndExpr(and *andExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseAndExpr")) - } - pt := p.pt - state := p.cloneState() p.pushV() _, ok := p.parseExpr(and.expr) p.popV() - p.restoreState(state) p.restore(pt) return nil, ok } func (p *parser) parseAnyMatcher(any *anyMatcher) (any, bool) { - if p.debug { - defer p.out(p.in("parseAnyMatcher")) - } - if p.pt.rn == utf8.RuneError && p.pt.w == 0 { // EOF - see utf8.DecodeRune p.failAt(false, p.pt.position, ".") @@ -2372,10 +3241,6 @@ func (p *parser) parseAnyMatcher(any *anyMatcher) (any, bool) { } func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (any, bool) { - if p.debug { - defer p.out(p.in("parseCharClassMatcher")) - } - cur := p.pt.rn start := p.pt @@ -2437,50 +3302,22 @@ func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (any, bool) { return nil, false } -func (p *parser) incChoiceAltCnt(ch *choiceExpr, altI int) { - choiceIdent := fmt.Sprintf("%s %d:%d", p.rstack[len(p.rstack)-1].name, ch.pos.line, ch.pos.col) - m := p.ChoiceAltCnt[choiceIdent] - if m == nil { - m = make(map[string]int) - p.ChoiceAltCnt[choiceIdent] = m - } - // We increment altI by 1, so the keys do not start at 0 - alt := strconv.Itoa(altI + 1) - if altI == choiceNoMatch { - alt = p.choiceNoMatch - } - m[alt]++ -} - func (p *parser) parseChoiceExpr(ch *choiceExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseChoiceExpr")) - } - for altI, alt := range ch.alternatives { // dummy assignment to prevent compile error if optimized _ = altI - state := p.cloneState() - p.pushV() val, ok := p.parseExpr(alt) p.popV() if ok { - p.incChoiceAltCnt(ch, altI) return val, ok } - p.restoreState(state) } - p.incChoiceAltCnt(ch, choiceNoMatch) return nil, false } func (p *parser) parseLabeledExpr(lab *labeledExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseLabeledExpr")) - } - p.pushV() val, ok := p.parseExpr(lab.expr) p.popV() @@ -2492,10 +3329,6 @@ func (p *parser) parseLabeledExpr(lab *labeledExpr) (any, bool) { } func (p *parser) parseLitMatcher(lit *litMatcher) (any, bool) { - if p.debug { - defer p.out(p.in("parseLitMatcher")) - } - start := p.pt for _, want := range lit.val { cur := p.pt.rn @@ -2514,44 +3347,27 @@ func (p *parser) parseLitMatcher(lit *litMatcher) (any, bool) { } func (p *parser) parseNotCodeExpr(not *notCodeExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseNotCodeExpr")) - } - - state := p.cloneState() - ok, err := not.run(p) if err != nil { p.addErr(err) } - p.restoreState(state) return nil, !ok } func (p *parser) parseNotExpr(not *notExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseNotExpr")) - } - pt := p.pt - state := p.cloneState() p.pushV() p.maxFailInvertExpected = !p.maxFailInvertExpected _, ok := p.parseExpr(not.expr) p.maxFailInvertExpected = !p.maxFailInvertExpected p.popV() - p.restoreState(state) p.restore(pt) return nil, !ok } func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseOneOrMoreExpr")) - } - var vals []any for { @@ -2570,9 +3386,6 @@ func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (any, bool) { } func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseRecoveryExpr (" + strings.Join(recover.failureLabel, ",") + ")")) - } p.pushRecovery(recover.failureLabel, recover.recoverExpr) val, ok := p.parseExpr(recover.expr) @@ -2582,10 +3395,6 @@ func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (any, bool) { } func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseRuleRefExpr " + ref.name)) - } - if ref.name == "" { panic(fmt.Sprintf("%s: invalid rule: missing name", ref.pos)) } @@ -2599,18 +3408,12 @@ func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (any, bool) { } func (p *parser) parseSeqExpr(seq *seqExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseSeqExpr")) - } - vals := make([]any, 0, len(seq.exprs)) pt := p.pt - state := p.cloneState() for _, expr := range seq.exprs { val, ok := p.parseExpr(expr) if !ok { - p.restoreState(state) p.restore(pt) return nil, false } @@ -2619,22 +3422,7 @@ func (p *parser) parseSeqExpr(seq *seqExpr) (any, bool) { return vals, true } -func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseStateCodeExpr")) - } - - err := state.run(p) - if err != nil { - p.addErr(err) - } - return nil, true -} - func (p *parser) parseThrowExpr(expr *throwExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseThrowExpr")) - } for i := len(p.recoveryStack) - 1; i >= 0; i-- { if recoverExpr, ok := p.recoveryStack[i][expr.label]; ok { @@ -2648,10 +3436,6 @@ func (p *parser) parseThrowExpr(expr *throwExpr) (any, bool) { } func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseZeroOrMoreExpr")) - } - var vals []any for { @@ -2666,10 +3450,6 @@ func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (any, bool) { } func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (any, bool) { - if p.debug { - defer p.out(p.in("parseZeroOrOneExpr")) - } - p.pushV() val, _ := p.parseExpr(expr.expr) p.popV() diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index a77d45f2c96..0d25f5facd2 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -1,7 +1,6 @@ package kql_test import ( - "errors" "strings" "testing" "time" @@ -81,12 +80,16 @@ func TestParse(t *testing.T) { }, }, { - name: `AND`, - expectedError: errors.New(""), + name: `AND`, + expectedError: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, }, { - name: `AND cat AND dog`, - expectedError: errors.New(""), + name: `AND cat AND dog`, + expectedError: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, }, // ++ // 2.1.6 NOT Operator @@ -125,12 +128,16 @@ func TestParse(t *testing.T) { }, }, { - name: `OR`, - expectedError: errors.New(""), + name: `OR`, + expectedError: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, }, { - name: `OR cat AND dog`, - expectedError: errors.New(""), + name: `OR cat AND dog`, + expectedError: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, }, // ++ // 3.1.11 Implicit Operator @@ -450,6 +457,7 @@ func TestParse(t *testing.T) { // everything else { name: "FullDictionary", + skip: true, givenQuery: mustJoin(FullDictionary), expectedAst: &ast.Ast{ Nodes: []ast.Node{ @@ -814,6 +822,87 @@ func TestParse(t *testing.T) { }, }, }, + { + name: "animal:(cat dog turtle)", + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "animal", + Nodes: []ast.Node{ + &ast.StringNode{ + Value: "cat", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "dog", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "turtle", + }, + }, + }, + }, + }, + }, + { + name: "(cat dog turtle)", + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{ + Value: "cat", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "dog", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "turtle", + }, + }, + }, + }, + }, + }, + { + name: "animal:(mammal:cat mammal:dog reptile:turtle)", + expectedError: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "cat"}, + }, + }, + { + name: "animal:(cat mammal:dog turtle)", + expectedError: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "dog"}, + }, + }, + { + name: "animal:(AND cat)", + expectedError: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + name: "animal:(OR cat)", + expectedError: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + name: "(AND cat)", + expectedError: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + name: "(OR cat)", + expectedError: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, } assert := tAssert.New(t) @@ -836,7 +925,7 @@ func TestParse(t *testing.T) { if tt.expectedError != nil { if tt.expectedError.Error() != "" { - assert.Equal(err, tt.expectedError) + assert.Equal(err.Error(), tt.expectedError.Error()) } else { assert.NotNil(err) } diff --git a/services/search/pkg/query/kql/factory.go b/services/search/pkg/query/kql/factory.go index 6b26f1f685a..05827184830 100644 --- a/services/search/pkg/query/kql/factory.go +++ b/services/search/pkg/query/kql/factory.go @@ -38,9 +38,23 @@ func buildAST(n interface{}, text []byte, pos position) (*ast.Ast, error) { return nil, err } + if len(nodes) == 0 { + return nil, nil + } + + nodes = connectNodes(DefaultConnector{sameKeyOPValue: BoolOR}, nodes...) + + switch node := nodes[0].(type) { + case *ast.OperatorNode: + switch node.Value { + case BoolAND, BoolOR: + return nil, StartsWithBinaryOperatorError{Node: node} + } + } + return &ast.Ast{ Base: b, - Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolOR}, nodes...), + Nodes: nodes, }, nil } @@ -156,9 +170,33 @@ func buildGroupNode(k, n interface{}, text []byte, pos position) (*ast.GroupNode return nil, err } - return &ast.GroupNode{ + nodes = connectNodes(DefaultConnector{sameKeyOPValue: BoolAND}, nodes...) + + gn := &ast.GroupNode{ Base: b, Key: key, - Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolAND}, nodes...), - }, nil + Nodes: nodes, + } + + if len(nodes) == 0 { + return gn, nil + } + + switch node := nodes[0].(type) { + case *ast.OperatorNode: + switch node.Value { + case BoolAND, BoolOR: + return nil, StartsWithBinaryOperatorError{Node: node} + } + } + + if key != "" { + for _, node := range nodes { + if ast.NodeKey(node) != "" { + return nil, NamedGroupInvalidNodesError{Node: node} + } + } + } + + return gn, nil } diff --git a/services/search/pkg/query/kql/kql.go b/services/search/pkg/query/kql/kql.go index 67565f59533..8719223b8d2 100644 --- a/services/search/pkg/query/kql/kql.go +++ b/services/search/pkg/query/kql/kql.go @@ -31,6 +31,10 @@ func (b Builder) Build(q string) (*ast.Ast, error) { var parserError *parserError switch { case errors.As(listError, &parserError): + if parserError.Inner != nil { + return nil, parserError.Inner + } + return nil, listError } } diff --git a/services/search/pkg/query/kql/kql_test.go b/services/search/pkg/query/kql/kql_test.go index 0e8e871221f..de050a47413 100644 --- a/services/search/pkg/query/kql/kql_test.go +++ b/services/search/pkg/query/kql/kql_test.go @@ -5,23 +5,26 @@ import ( tAssert "github.com/stretchr/testify/assert" + "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" "github.com/owncloud/ocis/v2/services/search/pkg/query/kql" ) func TestNewAST(t *testing.T) { tests := []struct { - name string - givenQuery string - shouldError bool + name string + givenQuery string + expectedError error }{ { name: "success", givenQuery: "foo:bar", }, { - name: "error", - givenQuery: kql.BoolAND, - shouldError: true, + name: "error", + givenQuery: kql.BoolAND, + expectedError: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, }, } @@ -32,13 +35,20 @@ func TestNewAST(t *testing.T) { t.Run(tt.name, func(t *testing.T) { got, err := kql.Builder{}.Build(tt.givenQuery) - if tt.shouldError { - assert.NotNil(err) + if tt.expectedError != nil { + if tt.expectedError.Error() != "" { + assert.Equal(err.Error(), tt.expectedError.Error()) + } else { + assert.NotNil(err) + } + assert.Nil(got) - } else { - assert.Nil(err) - assert.NotNil(got) + + return } + + assert.Nil(err) + assert.NotNil(got) }) } } From 9bb6dc509af527070fed195fd3d5868307761296 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Sat, 9 Sep 2023 23:28:59 +0200 Subject: [PATCH 06/11] enhancement: use optimized grammar for kql parser and toolify pigeon --- services/search/pkg/query/kql/connect.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/services/search/pkg/query/kql/connect.go b/services/search/pkg/query/kql/connect.go index e592de8544a..fa0ef00829f 100644 --- a/services/search/pkg/query/kql/connect.go +++ b/services/search/pkg/query/kql/connect.go @@ -13,13 +13,12 @@ func connectNodes(c Connector, nodes ...ast.Node) []ast.Node { for i := range nodes { ri := len(nodes) - 1 - i head := nodes[ri] - pair := []ast.Node{head} - if connectionNodes := connectNode(c, pair[0], connectedNodes...); len(connectionNodes) >= 1 { - pair = append(pair, connectionNodes...) + if connectionNodes := connectNode(c, head, connectedNodes...); len(connectionNodes) > 0 { + connectedNodes = append(connectionNodes, connectedNodes...) } - connectedNodes = append(pair, connectedNodes...) + connectedNodes = append([]ast.Node{head}, connectedNodes...) } return connectedNodes @@ -83,8 +82,8 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections // author:"John Smith" author:"Jane Smith" // author:"John Smith" OR author:"Jane Smith" // - // nodes inside of group nodes are handled differently, - // if no explicit operator give, it uses OR + // nodes inside of group node are handled differently, + // if no explicit operator given, it uses OR // // spec: same // author:"John Smith" AND author:"Jane Smith" @@ -95,12 +94,12 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections // decisions based on nearest neighbor node switch neighbor.(type) { - // nearest neighbor node type could change the default case + // nearest neighbor node type can change the default case // docs says, if the next value node: // - // is a group AND has no key + // is a group and has no key // - // even if the current node has none too, which normal leads to SAME KEY OR + // and the head node has no key // // it should be an AND edge // @@ -128,7 +127,7 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections } } - // if neighbor node negotiates, AND edge is needed + // if neighbor node negotiates, an AND edge is needed // // spec: same // cat -dog From 1909651e91bab82f855d68c54a64390a100b70e9 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Sun, 10 Sep 2023 12:22:53 +0200 Subject: [PATCH 07/11] enhancement: simplify error handling --- services/search/pkg/query/kql/factory.go | 44 +++++------------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/services/search/pkg/query/kql/factory.go b/services/search/pkg/query/kql/factory.go index 05827184830..bab2d945ae6 100644 --- a/services/search/pkg/query/kql/factory.go +++ b/services/search/pkg/query/kql/factory.go @@ -38,24 +38,16 @@ func buildAST(n interface{}, text []byte, pos position) (*ast.Ast, error) { return nil, err } - if len(nodes) == 0 { - return nil, nil + a := &ast.Ast{ + Base: b, + Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolOR}, nodes...), } - nodes = connectNodes(DefaultConnector{sameKeyOPValue: BoolOR}, nodes...) - - switch node := nodes[0].(type) { - case *ast.OperatorNode: - switch node.Value { - case BoolAND, BoolOR: - return nil, StartsWithBinaryOperatorError{Node: node} - } + if err := validateAst(a); err != nil { + return nil, err } - return &ast.Ast{ - Base: b, - Nodes: nodes, - }, nil + return a, nil } func buildStringNode(k, v interface{}, text []byte, pos position) (*ast.StringNode, error) { @@ -170,32 +162,14 @@ func buildGroupNode(k, n interface{}, text []byte, pos position) (*ast.GroupNode return nil, err } - nodes = connectNodes(DefaultConnector{sameKeyOPValue: BoolAND}, nodes...) - gn := &ast.GroupNode{ Base: b, Key: key, - Nodes: nodes, + Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolAND}, nodes...), } - if len(nodes) == 0 { - return gn, nil - } - - switch node := nodes[0].(type) { - case *ast.OperatorNode: - switch node.Value { - case BoolAND, BoolOR: - return nil, StartsWithBinaryOperatorError{Node: node} - } - } - - if key != "" { - for _, node := range nodes { - if ast.NodeKey(node) != "" { - return nil, NamedGroupInvalidNodesError{Node: node} - } - } + if err := validateGroupNode(gn); err != nil { + return nil, err } return gn, nil From 6d37fc532cbbcea5dba9df360b553a785c7a165d Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Mon, 11 Sep 2023 12:27:33 +0200 Subject: [PATCH 08/11] fix: kql implicit 'AND' and 'OR' follows the ms html spec instead of the pdf spec --- services/search/pkg/query/kql/connect.go | 43 ++--- .../search/pkg/query/kql/dictionary_test.go | 150 +++++++++++++++--- services/search/pkg/query/kql/doc.go | 1 + services/search/pkg/query/kql/factory.go | 2 +- 4 files changed, 148 insertions(+), 48 deletions(-) diff --git a/services/search/pkg/query/kql/connect.go b/services/search/pkg/query/kql/connect.go index fa0ef00829f..3c926d688ce 100644 --- a/services/search/pkg/query/kql/connect.go +++ b/services/search/pkg/query/kql/connect.go @@ -74,47 +74,34 @@ func (c DefaultConnector) Connect(head ast.Node, neighbor ast.Node, connections } // if the current node and the neighbor node have the same key - // the connection is of type OR, same applies if no keys are in place - // - // "" == "" + // the connection is of type OR // // spec: same // author:"John Smith" author:"Jane Smith" // author:"John Smith" OR author:"Jane Smith" // + // if the nodes have NO key, the edge is a AND connection + // + // spec: same + // cat dog + // cat AND dog + // from the spec: + // To construct complex queries, you can combine multiple + // free-text expressions with KQL query operators. + // If there are multiple free-text expressions without any + // operators in between them, the query behavior is the same + // as using the AND operator. + // // nodes inside of group node are handled differently, - // if no explicit operator given, it uses OR + // if no explicit operator given, it uses AND // // spec: same // author:"John Smith" AND author:"Jane Smith" // author:("John Smith" "Jane Smith") - if headKey == neighborKey { + if headKey == neighborKey && headKey != "" && neighborKey != "" { connection.Value = c.sameKeyOPValue } - // decisions based on nearest neighbor node - switch neighbor.(type) { - // nearest neighbor node type can change the default case - // docs says, if the next value node: - // - // is a group and has no key - // - // and the head node has no key - // - // it should be an AND edge - // - // spec: same - // cat (dog OR fox) - // cat AND (dog OR fox) - // - // note: - // sounds contradictory to me - case *ast.GroupNode: - if headKey == "" && neighborKey == "" { - connection.Value = BoolAND - } - } - // decisions based on nearest neighbor operators for i, node := range connections { // consider direct neighbor operator only diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index 0d25f5facd2..7e86456a999 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -65,6 +65,7 @@ func TestParse(t *testing.T) { // // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 + // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference // // ++ // 2.1.2 AND Operator @@ -146,7 +147,7 @@ func TestParse(t *testing.T) { expectedAst: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "dog"}, }, }, @@ -333,7 +334,7 @@ func TestParse(t *testing.T) { expectedAst: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "dog"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "fox"}, @@ -363,7 +364,7 @@ func TestParse(t *testing.T) { expectedAst: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "dog"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.OperatorNode{Value: kql.BoolNOT}, @@ -457,20 +458,19 @@ func TestParse(t *testing.T) { // everything else { name: "FullDictionary", - skip: true, givenQuery: mustJoin(FullDictionary), expectedAst: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "federated"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "federat*"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "fed*"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Key: "author", Value: "John Smith"}, @@ -480,23 +480,23 @@ func TestParse(t *testing.T) { &ast.StringNode{Key: "filename", Value: "budget.xlsx"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Key: "author", Value: "Shakespear"}, @@ -689,7 +689,7 @@ func TestParse(t *testing.T) { &ast.StringNode{ Value: "๐Ÿ˜‚", }, - &ast.OperatorNode{Value: kql.BoolOR}, + &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{ Value: "*๐Ÿ˜€ ๐Ÿ˜*", }, @@ -903,6 +903,118 @@ func TestParse(t *testing.T) { Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, + { + name: `cat dog`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + }, + { + name: `cat dog fox`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + name: `(cat dog) fox`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + name: `(mammal:cat mammal:dog) fox`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "mammal", Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "mammal", Value: "dog"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + name: `mammal:(cat dog) fox`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "mammal", + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + name: `mammal:(cat dog) mammal:fox`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "mammal", + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "mammal", Value: "fox"}, + }, + }, + }, + { + name: `title:((Advanced OR Search OR Query) -"Advanced Search Query")`, + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "title", + Nodes: []ast.Node{ + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "Advanced"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Search"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Query"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "Advanced Search Query"}, + }, + }, + }, + }, + }, } assert := tAssert.New(t) diff --git a/services/search/pkg/query/kql/doc.go b/services/search/pkg/query/kql/doc.go index 577ada26b72..a887affc32f 100644 --- a/services/search/pkg/query/kql/doc.go +++ b/services/search/pkg/query/kql/doc.go @@ -21,6 +21,7 @@ The following spec parts are supported and tested: - 3.3.5 Date Tokens References: + - https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference - https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 - https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf */ diff --git a/services/search/pkg/query/kql/factory.go b/services/search/pkg/query/kql/factory.go index bab2d945ae6..ff9044886a9 100644 --- a/services/search/pkg/query/kql/factory.go +++ b/services/search/pkg/query/kql/factory.go @@ -165,7 +165,7 @@ func buildGroupNode(k, n interface{}, text []byte, pos position) (*ast.GroupNode gn := &ast.GroupNode{ Base: b, Key: key, - Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolAND}, nodes...), + Nodes: connectNodes(DefaultConnector{sameKeyOPValue: BoolOR}, nodes...), } if err := validateGroupNode(gn); err != nil { From e169be57e4615f167268fc38320f7a5e70625bea Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Sun, 10 Sep 2023 17:40:47 +0200 Subject: [PATCH 09/11] enhancement: add support for natural language kql date queries --- go.mod | 2 +- go.sum | 7 +- services/search/pkg/query/kql/cast.go | 68 +- services/search/pkg/query/kql/dictionary.peg | 21 +- .../search/pkg/query/kql/dictionary_gen.go | 796 +++--- .../search/pkg/query/kql/dictionary_test.go | 249 +- services/search/pkg/query/kql/doc.go | 1 - .../search/pkg/query/kql/engine_suite_test.go | 11 + services/search/pkg/query/kql/error.go | 12 + services/search/pkg/query/kql/factory.go | 33 + services/search/pkg/query/kql/kql.go | 5 + .../github.com/araddon/dateparse/.travis.yml | 13 - vendor/github.com/araddon/dateparse/README.md | 323 --- .../github.com/araddon/dateparse/parseany.go | 2189 ----------------- vendor/github.com/jinzhu/now/Guardfile | 3 + .../dateparse/LICENSE => jinzhu/now/License} | 2 +- vendor/github.com/jinzhu/now/README.md | 137 ++ vendor/github.com/jinzhu/now/main.go | 200 ++ vendor/github.com/jinzhu/now/now.go | 245 ++ vendor/github.com/jinzhu/now/time.go | 9 + vendor/modules.txt | 6 +- 21 files changed, 1468 insertions(+), 2864 deletions(-) create mode 100644 services/search/pkg/query/kql/engine_suite_test.go delete mode 100644 vendor/github.com/araddon/dateparse/.travis.yml delete mode 100644 vendor/github.com/araddon/dateparse/README.md delete mode 100644 vendor/github.com/araddon/dateparse/parseany.go create mode 100644 vendor/github.com/jinzhu/now/Guardfile rename vendor/github.com/{araddon/dateparse/LICENSE => jinzhu/now/License} (95%) create mode 100644 vendor/github.com/jinzhu/now/README.md create mode 100644 vendor/github.com/jinzhu/now/main.go create mode 100644 vendor/github.com/jinzhu/now/now.go create mode 100644 vendor/github.com/jinzhu/now/time.go diff --git a/go.mod b/go.mod index eb7dfc7d5b9..5abf43305a1 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/Masterminds/semver v1.5.0 github.com/MicahParks/keyfunc v1.9.0 github.com/Nerzal/gocloak/v13 v13.8.0 - github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bbalet/stopwords v1.0.0 github.com/blevesearch/bleve/v2 v2.3.9 github.com/coreos/go-oidc v2.2.1+incompatible @@ -50,6 +49,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 github.com/jellydator/ttlcache/v2 v2.11.1 github.com/jellydator/ttlcache/v3 v3.1.0 + github.com/jinzhu/now v1.1.5 github.com/justinas/alice v1.2.0 github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5 diff --git a/go.sum b/go.sum index dc4fc443a7e..943055abbff 100644 --- a/go.sum +++ b/go.sum @@ -859,8 +859,6 @@ github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4x github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= -github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -1525,6 +1523,8 @@ github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVK github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -1642,7 +1642,6 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -1866,7 +1865,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/riandyrn/otelchi v0.5.1 h1:0/45omeqpP7f/cvdL16GddQBfAEmZvUyl2QzLSE6uYo= github.com/riandyrn/otelchi v0.5.1/go.mod h1:ZxVxNEl+jQ9uHseRYIxKWRb3OY8YXFEu+EkNiiSNUEA= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -1898,7 +1896,6 @@ github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/sciencemesh/meshdirectory-web v1.0.4 h1:1YSctF6PAXhoHUYCaeRTj7rHaF7b3rYrZf2R0VXBIbo= github.com/sciencemesh/meshdirectory-web v1.0.4/go.mod h1:fJSThTS3xf+sTdL0iXQoaQJssLI7tn7DetHMHUl4SRk= -github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= diff --git a/services/search/pkg/query/kql/cast.go b/services/search/pkg/query/kql/cast.go index d9710d35ce1..60c42040898 100644 --- a/services/search/pkg/query/kql/cast.go +++ b/services/search/pkg/query/kql/cast.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/araddon/dateparse" + "github.com/jinzhu/now" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -21,21 +21,23 @@ func toNode[T ast.Node](in interface{}) (T, error) { func toNodes[T ast.Node](in interface{}) ([]T, error) { switch v := in.(type) { - case []T: - return v, nil case T: return []T{v}, nil + case []T: + return v, nil + case []*ast.OperatorNode, []*ast.DateTimeNode: + return toNodes[T](v) case []interface{}: - var ts []T - for _, inter := range v { - n, err := toNodes[T](inter) + var nodes []T + for _, el := range v { + node, err := toNodes[T](el) if err != nil { return nil, err } - ts = append(ts, n...) + nodes = append(nodes, node...) } - return ts, nil + return nodes, nil case nil: return nil, nil default: @@ -74,5 +76,53 @@ func toTime(in interface{}) (time.Time, error) { return time.Time{}, err } - return dateparse.ParseLocal(ts) + return now.Parse(ts) +} + +func toTimeRange(in interface{}) (*time.Time, *time.Time, error) { + var from, to time.Time + + value, err := toString(in) + if err != nil { + return &from, &to, UnsupportedTimeRange{} + } + + c := &now.Config{ + WeekStartDay: time.Monday, + } + + n := c.With(timeNow()) + + switch value { + case "today": + from = n.BeginningOfDay() + to = n.EndOfDay() + case "yesterday": + yesterday := n.With(n.AddDate(0, 0, -1)) + from = yesterday.BeginningOfDay() + to = yesterday.EndOfDay() + case "this week": + from = n.BeginningOfWeek() + to = n.EndOfWeek() + case "this month": + from = n.BeginningOfMonth() + to = n.EndOfMonth() + case "last month": + lastMonth := n.With(n.AddDate(0, -1, 0)) + from = lastMonth.BeginningOfMonth() + to = lastMonth.EndOfMonth() + case "this year": + from = n.BeginningOfYear() + to = n.EndOfYear() + case "last year": + lastYear := n.With(n.AddDate(-1, 0, 0)) + from = lastYear.BeginningOfYear() + to = lastYear.EndOfYear() + } + + if from.IsZero() || to.IsZero() { + return nil, nil, UnsupportedTimeRange{} + } + + return &from, &to, nil } diff --git a/services/search/pkg/query/kql/dictionary.peg b/services/search/pkg/query/kql/dictionary.peg index ca9658dfa53..426e4d5091f 100644 --- a/services/search/pkg/query/kql/dictionary.peg +++ b/services/search/pkg/query/kql/dictionary.peg @@ -61,6 +61,12 @@ DateTimeRestrictionNode <- FullTime ) '"'? { return buildDateTimeNode(k, o, v, c.text, c.pos) + } / + k:Char+ ( + OperatorEqualNode / + OperatorColonNode + ) '"'? v:NaturalLanguageDateTime '"'? { + return buildNaturalLanguageDateTimeNodes(k, v, c.text, c.pos) } TextPropertyRestrictionNode <- @@ -185,11 +191,22 @@ FullTime <- return c.text, nil } -DateTime - = FullDate "T" FullTime { +DateTime <- + FullDate "T" FullTime { return c.text, nil } +NaturalLanguageDateTime <- + "today" / + "yesterday" / + "this week" / + "this month" / + "last month" / + "this year" / + "last year" { + return c.text, nil + } + //////////////////////////////////////////////////////// // misc //////////////////////////////////////////////////////// diff --git a/services/search/pkg/query/kql/dictionary_gen.go b/services/search/pkg/query/kql/dictionary_gen.go index afce6c5c85e..4bcd4f83b02 100644 --- a/services/search/pkg/query/kql/dictionary_gen.go +++ b/services/search/pkg/query/kql/dictionary_gen.go @@ -43,12 +43,12 @@ var g = &grammar{ pos: position{line: 19, col: 6, offset: 351}, exprs: []any{ &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, run: (*parser).callonNodes3, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -86,10 +86,10 @@ var g = &grammar{ expr: &oneOrMoreExpr{ pos: position{line: 46, col: 7, offset: 1044}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonNode7, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -102,20 +102,20 @@ var g = &grammar{ pos: position{line: 46, col: 14, offset: 1051}, alternatives: []any{ &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, run: (*parser).callonNode10, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonNode12, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", @@ -159,10 +159,10 @@ var g = &grammar{ expr: &oneOrMoreExpr{ pos: position{line: 51, col: 7, offset: 1205}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonNode22, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -178,60 +178,60 @@ var g = &grammar{ pos: position{line: 52, col: 9, offset: 1223}, alternatives: []any{ &actionExpr{ - pos: position{line: 139, col: 5, offset: 3168}, + pos: position{line: 145, col: 5, offset: 3358}, run: (*parser).callonNode26, expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3168}, + pos: position{line: 145, col: 5, offset: 3358}, val: ">=", ignoreCase: false, want: "\">=\"", }, }, &actionExpr{ - pos: position{line: 129, col: 5, offset: 2984}, + pos: position{line: 135, col: 5, offset: 3174}, run: (*parser).callonNode28, expr: &litMatcher{ - pos: position{line: 129, col: 5, offset: 2984}, + pos: position{line: 135, col: 5, offset: 3174}, val: "<=", ignoreCase: false, want: "\"<=\"", }, }, &actionExpr{ - pos: position{line: 134, col: 5, offset: 3073}, + pos: position{line: 140, col: 5, offset: 3263}, run: (*parser).callonNode30, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3073}, + pos: position{line: 140, col: 5, offset: 3263}, val: ">", ignoreCase: false, want: "\">\"", }, }, &actionExpr{ - pos: position{line: 124, col: 5, offset: 2892}, + pos: position{line: 130, col: 5, offset: 3082}, run: (*parser).callonNode32, expr: &litMatcher{ - pos: position{line: 124, col: 5, offset: 2892}, + pos: position{line: 130, col: 5, offset: 3082}, val: "<", ignoreCase: false, want: "\"<\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonNode34, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", }, }, &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, run: (*parser).callonNode36, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -256,28 +256,28 @@ var g = &grammar{ pos: position{line: 59, col: 9, offset: 1420}, alternatives: []any{ &actionExpr{ - pos: position{line: 189, col: 5, offset: 4003}, + pos: position{line: 195, col: 5, offset: 4197}, run: (*parser).callonNode42, expr: &seqExpr{ - pos: position{line: 189, col: 5, offset: 4003}, + pos: position{line: 195, col: 5, offset: 4197}, exprs: []any{ &actionExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, run: (*parser).callonNode44, expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, exprs: []any{ &actionExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, run: (*parser).callonNode46, expr: &seqExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode48, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -285,10 +285,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode50, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -296,10 +296,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode52, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -307,10 +307,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode54, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -321,22 +321,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 14, offset: 3779}, + pos: position{line: 185, col: 14, offset: 3969}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, run: (*parser).callonNode57, expr: &seqExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode59, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -344,10 +344,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode61, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -358,22 +358,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 28, offset: 3793}, + pos: position{line: 185, col: 28, offset: 3983}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, run: (*parser).callonNode64, expr: &seqExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode66, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -381,10 +381,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode68, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -398,28 +398,28 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 189, col: 14, offset: 4012}, + pos: position{line: 195, col: 14, offset: 4206}, val: "T", ignoreCase: false, want: "\"T\"", }, &actionExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, run: (*parser).callonNode71, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, exprs: []any{ &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode73, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode75, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -427,10 +427,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode77, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -441,22 +441,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 14, offset: 3866}, + pos: position{line: 190, col: 14, offset: 4056}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode80, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode82, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -464,10 +464,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode84, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -478,22 +478,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 29, offset: 3881}, + pos: position{line: 190, col: 29, offset: 4071}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, run: (*parser).callonNode87, expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode89, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -501,10 +501,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode91, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -515,23 +515,23 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 184, col: 44, offset: 3896}, + pos: position{line: 190, col: 44, offset: 4086}, expr: &seqExpr{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, exprs: []any{ &litMatcher{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 184, col: 49, offset: 3901}, + pos: position{line: 190, col: 49, offset: 4091}, expr: &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode97, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -543,35 +543,35 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, alternatives: []any{ &litMatcher{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 184, col: 65, offset: 3917}, + pos: position{line: 190, col: 65, offset: 4107}, exprs: []any{ &charClassMatcher{ - pos: position{line: 184, col: 66, offset: 3918}, + pos: position{line: 190, col: 66, offset: 4108}, val: "[+-]", chars: []rune{'+', '-'}, ignoreCase: false, inverted: false, }, &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode103, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode105, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -579,10 +579,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode107, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -593,22 +593,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 86, offset: 3938}, + pos: position{line: 190, col: 86, offset: 4128}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode110, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode112, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -616,10 +616,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode114, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -640,22 +640,22 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, run: (*parser).callonNode116, expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, exprs: []any{ &actionExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, run: (*parser).callonNode118, expr: &seqExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode120, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -663,10 +663,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode122, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -674,10 +674,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode124, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -685,10 +685,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode126, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -699,22 +699,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 14, offset: 3779}, + pos: position{line: 185, col: 14, offset: 3969}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, run: (*parser).callonNode129, expr: &seqExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode131, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -722,10 +722,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode133, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -736,22 +736,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 28, offset: 3793}, + pos: position{line: 185, col: 28, offset: 3983}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, run: (*parser).callonNode136, expr: &seqExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode138, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -759,10 +759,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode140, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -776,22 +776,22 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, run: (*parser).callonNode142, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, exprs: []any{ &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode144, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode146, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -799,10 +799,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode148, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -813,22 +813,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 14, offset: 3866}, + pos: position{line: 190, col: 14, offset: 4056}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode151, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode153, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -836,10 +836,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode155, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -850,22 +850,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 29, offset: 3881}, + pos: position{line: 190, col: 29, offset: 4071}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, run: (*parser).callonNode158, expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode160, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -873,10 +873,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode162, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -887,23 +887,23 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 184, col: 44, offset: 3896}, + pos: position{line: 190, col: 44, offset: 4086}, expr: &seqExpr{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, exprs: []any{ &litMatcher{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 184, col: 49, offset: 3901}, + pos: position{line: 190, col: 49, offset: 4091}, expr: &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode168, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -915,35 +915,35 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, alternatives: []any{ &litMatcher{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 184, col: 65, offset: 3917}, + pos: position{line: 190, col: 65, offset: 4107}, exprs: []any{ &charClassMatcher{ - pos: position{line: 184, col: 66, offset: 3918}, + pos: position{line: 190, col: 66, offset: 4108}, val: "[+-]", chars: []rune{'+', '-'}, ignoreCase: false, inverted: false, }, &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode174, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode176, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -951,10 +951,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode178, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -965,22 +965,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 86, offset: 3938}, + pos: position{line: 190, col: 86, offset: 4128}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode181, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode183, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -988,10 +988,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode185, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1024,21 +1024,21 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 67, col: 5, offset: 1579}, + pos: position{line: 65, col: 5, offset: 1549}, run: (*parser).callonNode189, expr: &seqExpr{ - pos: position{line: 67, col: 5, offset: 1579}, + pos: position{line: 65, col: 5, offset: 1549}, exprs: []any{ &labeledExpr{ - pos: position{line: 67, col: 5, offset: 1579}, + pos: position{line: 65, col: 5, offset: 1549}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 67, col: 7, offset: 1581}, + pos: position{line: 65, col: 7, offset: 1551}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonNode193, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1048,23 +1048,148 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 67, col: 14, offset: 1588}, + pos: position{line: 66, col: 9, offset: 1567}, alternatives: []any{ &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonNode196, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 125, col: 5, offset: 2997}, + val: "=", + ignoreCase: false, + want: "\"=\"", + }, + }, + &actionExpr{ + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode198, + expr: &litMatcher{ + pos: position{line: 120, col: 5, offset: 2911}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 68, col: 7, offset: 1619}, + expr: &litMatcher{ + pos: position{line: 68, col: 7, offset: 1619}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + &labeledExpr{ + pos: position{line: 68, col: 12, offset: 1624}, + label: "v", + expr: &choiceExpr{ + pos: position{line: 200, col: 5, offset: 4285}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 200, col: 5, offset: 4285}, + val: "today", + ignoreCase: false, + want: "\"today\"", + }, + &litMatcher{ + pos: position{line: 201, col: 5, offset: 4299}, + val: "yesterday", + ignoreCase: false, + want: "\"yesterday\"", + }, + &litMatcher{ + pos: position{line: 202, col: 5, offset: 4317}, + val: "this week", + ignoreCase: false, + want: "\"this week\"", + }, + &litMatcher{ + pos: position{line: 203, col: 5, offset: 4335}, + val: "this month", + ignoreCase: false, + want: "\"this month\"", + }, + &litMatcher{ + pos: position{line: 204, col: 5, offset: 4354}, + val: "last month", + ignoreCase: false, + want: "\"last month\"", + }, + &litMatcher{ + pos: position{line: 205, col: 5, offset: 4373}, + val: "this year", + ignoreCase: false, + want: "\"this year\"", + }, + &actionExpr{ + pos: position{line: 206, col: 5, offset: 4391}, + run: (*parser).callonNode210, + expr: &litMatcher{ + pos: position{line: 206, col: 5, offset: 4391}, + val: "last year", + ignoreCase: false, + want: "\"last year\"", + }, + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 68, col: 38, offset: 1650}, + expr: &litMatcher{ + pos: position{line: 68, col: 38, offset: 1650}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 73, col: 5, offset: 1769}, + run: (*parser).callonNode214, + expr: &seqExpr{ + pos: position{line: 73, col: 5, offset: 1769}, + exprs: []any{ + &labeledExpr{ + pos: position{line: 73, col: 5, offset: 1769}, + label: "k", + expr: &oneOrMoreExpr{ + pos: position{line: 73, col: 7, offset: 1771}, + expr: &actionExpr{ + pos: position{line: 215, col: 5, offset: 4575}, + run: (*parser).callonNode218, + expr: &charClassMatcher{ + pos: position{line: 215, col: 5, offset: 4575}, + val: "[A-Za-z]", + ranges: []rune{'A', 'Z', 'a', 'z'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + &choiceExpr{ + pos: position{line: 73, col: 14, offset: 1778}, + alternatives: []any{ + &actionExpr{ + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode221, + expr: &litMatcher{ + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, - run: (*parser).callonNode198, + pos: position{line: 125, col: 5, offset: 2997}, + run: (*parser).callonNode223, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", @@ -1073,30 +1198,30 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 67, col: 53, offset: 1627}, + pos: position{line: 73, col: 53, offset: 1817}, label: "v", expr: &choiceExpr{ - pos: position{line: 67, col: 56, offset: 1630}, + pos: position{line: 73, col: 56, offset: 1820}, alternatives: []any{ &actionExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - run: (*parser).callonNode202, + pos: position{line: 220, col: 5, offset: 4634}, + run: (*parser).callonNode227, expr: &seqExpr{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, exprs: []any{ &litMatcher{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 203, col: 9, offset: 4257}, + pos: position{line: 220, col: 9, offset: 4638}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, expr: &charClassMatcher{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1105,7 +1230,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 203, col: 17, offset: 4265}, + pos: position{line: 220, col: 17, offset: 4646}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1114,9 +1239,9 @@ var g = &grammar{ }, }, &oneOrMoreExpr{ - pos: position{line: 67, col: 65, offset: 1639}, + pos: position{line: 73, col: 65, offset: 1829}, expr: &charClassMatcher{ - pos: position{line: 67, col: 65, offset: 1639}, + pos: position{line: 73, col: 65, offset: 1829}, val: "[^ ()]", chars: []rune{' ', '(', ')'}, ignoreCase: false, @@ -1130,19 +1255,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 99, col: 5, offset: 2431}, - run: (*parser).callonNode211, + pos: position{line: 105, col: 5, offset: 2621}, + run: (*parser).callonNode236, expr: &choiceExpr{ - pos: position{line: 99, col: 6, offset: 2432}, + pos: position{line: 105, col: 6, offset: 2622}, alternatives: []any{ &litMatcher{ - pos: position{line: 99, col: 6, offset: 2432}, + pos: position{line: 105, col: 6, offset: 2622}, val: "AND", ignoreCase: false, want: "\"AND\"", }, &litMatcher{ - pos: position{line: 99, col: 14, offset: 2440}, + pos: position{line: 105, col: 14, offset: 2630}, val: "+", ignoreCase: false, want: "\"+\"", @@ -1151,19 +1276,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 104, col: 5, offset: 2532}, - run: (*parser).callonNode215, + pos: position{line: 110, col: 5, offset: 2722}, + run: (*parser).callonNode240, expr: &choiceExpr{ - pos: position{line: 104, col: 6, offset: 2533}, + pos: position{line: 110, col: 6, offset: 2723}, alternatives: []any{ &litMatcher{ - pos: position{line: 104, col: 6, offset: 2533}, + pos: position{line: 110, col: 6, offset: 2723}, val: "NOT", ignoreCase: false, want: "\"NOT\"", }, &litMatcher{ - pos: position{line: 104, col: 14, offset: 2541}, + pos: position{line: 110, col: 14, offset: 2731}, val: "-", ignoreCase: false, want: "\"-\"", @@ -1172,28 +1297,28 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 109, col: 5, offset: 2632}, - run: (*parser).callonNode219, + pos: position{line: 115, col: 5, offset: 2822}, + run: (*parser).callonNode244, expr: &litMatcher{ - pos: position{line: 109, col: 6, offset: 2633}, + pos: position{line: 115, col: 6, offset: 2823}, val: "OR", ignoreCase: false, want: "\"OR\"", }, }, &actionExpr{ - pos: position{line: 80, col: 6, offset: 1919}, - run: (*parser).callonNode221, + pos: position{line: 86, col: 6, offset: 2109}, + run: (*parser).callonNode246, expr: &seqExpr{ - pos: position{line: 80, col: 6, offset: 1919}, + pos: position{line: 86, col: 6, offset: 2109}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 80, col: 6, offset: 1919}, + pos: position{line: 86, col: 6, offset: 2109}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode224, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode249, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1201,12 +1326,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode226, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode251, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1215,27 +1340,27 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 80, col: 27, offset: 1940}, + pos: position{line: 86, col: 27, offset: 2130}, label: "v", expr: &actionExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - run: (*parser).callonNode230, + pos: position{line: 220, col: 5, offset: 4634}, + run: (*parser).callonNode255, expr: &seqExpr{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, exprs: []any{ &litMatcher{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 203, col: 9, offset: 4257}, + pos: position{line: 220, col: 9, offset: 4638}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, expr: &charClassMatcher{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1244,7 +1369,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 203, col: 17, offset: 4265}, + pos: position{line: 220, col: 17, offset: 4646}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1254,12 +1379,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode237, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode262, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1268,12 +1393,12 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 80, col: 38, offset: 1951}, + pos: position{line: 86, col: 38, offset: 2141}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode241, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode266, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1284,18 +1409,18 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 85, col: 6, offset: 2049}, - run: (*parser).callonNode243, + pos: position{line: 91, col: 6, offset: 2239}, + run: (*parser).callonNode268, expr: &seqExpr{ - pos: position{line: 85, col: 6, offset: 2049}, + pos: position{line: 91, col: 6, offset: 2239}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 85, col: 6, offset: 2049}, + pos: position{line: 91, col: 6, offset: 2239}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode246, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode271, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1303,12 +1428,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode248, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode273, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1317,12 +1442,12 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 85, col: 27, offset: 2070}, + pos: position{line: 91, col: 27, offset: 2260}, label: "v", expr: &oneOrMoreExpr{ - pos: position{line: 85, col: 29, offset: 2072}, + pos: position{line: 91, col: 29, offset: 2262}, expr: &charClassMatcher{ - pos: position{line: 85, col: 29, offset: 2072}, + pos: position{line: 91, col: 29, offset: 2262}, val: "[^ :()]", chars: []rune{' ', ':', '(', ')'}, ignoreCase: false, @@ -1331,12 +1456,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode254, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode279, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1345,12 +1470,12 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 85, col: 40, offset: 2083}, + pos: position{line: 91, col: 40, offset: 2273}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode258, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode283, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1380,10 +1505,10 @@ var g = &grammar{ expr: &oneOrMoreExpr{ pos: position{line: 32, col: 8, offset: 615}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonGroupNode6, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1399,20 +1524,20 @@ var g = &grammar{ pos: position{line: 32, col: 17, offset: 624}, alternatives: []any{ &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, run: (*parser).callonGroupNode10, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonGroupNode12, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", @@ -2284,19 +2409,19 @@ func (p *parser) callonNode198() (any, error) { return p.cur.onNode198() } -func (c *current) onNode202(v any) (any, error) { - return v, nil +func (c *current) onNode210() (any, error) { + return c.text, nil } -func (p *parser) callonNode202() (any, error) { +func (p *parser) callonNode210() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode202(stack["v"]) + return p.cur.onNode210() } func (c *current) onNode189(k, v any) (any, error) { - return buildStringNode(k, v, c.text, c.pos) + return buildNaturalLanguageDateTimeNodes(k, v, c.text, c.pos) } @@ -2306,158 +2431,213 @@ func (p *parser) callonNode189() (any, error) { return p.cur.onNode189(stack["k"], stack["v"]) } -func (c *current) onNode211() (any, error) { +func (c *current) onNode218() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode218() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode218() +} + +func (c *current) onNode221() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode211() (any, error) { +func (p *parser) callonNode221() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode211() + return p.cur.onNode221() } -func (c *current) onNode215() (any, error) { +func (c *current) onNode223() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode215() (any, error) { +func (p *parser) callonNode223() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode215() + return p.cur.onNode223() } -func (c *current) onNode219() (any, error) { +func (c *current) onNode227(v any) (any, error) { + return v, nil + +} + +func (p *parser) callonNode227() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode227(stack["v"]) +} + +func (c *current) onNode214(k, v any) (any, error) { + return buildStringNode(k, v, c.text, c.pos) + +} + +func (p *parser) callonNode214() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode214(stack["k"], stack["v"]) +} + +func (c *current) onNode236() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode236() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode236() +} + +func (c *current) onNode240() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode240() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode240() +} + +func (c *current) onNode244() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode219() (any, error) { +func (p *parser) callonNode244() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode219() + return p.cur.onNode244() } -func (c *current) onNode224() (any, error) { +func (c *current) onNode249() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode224() (any, error) { +func (p *parser) callonNode249() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode224() + return p.cur.onNode249() } -func (c *current) onNode226() (any, error) { +func (c *current) onNode251() (any, error) { return nil, nil } -func (p *parser) callonNode226() (any, error) { +func (p *parser) callonNode251() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode226() + return p.cur.onNode251() } -func (c *current) onNode230(v any) (any, error) { +func (c *current) onNode255(v any) (any, error) { return v, nil } -func (p *parser) callonNode230() (any, error) { +func (p *parser) callonNode255() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode230(stack["v"]) + return p.cur.onNode255(stack["v"]) } -func (c *current) onNode237(v any) (any, error) { +func (c *current) onNode262(v any) (any, error) { return nil, nil } -func (p *parser) callonNode237() (any, error) { +func (p *parser) callonNode262() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode237(stack["v"]) + return p.cur.onNode262(stack["v"]) } -func (c *current) onNode241() (any, error) { +func (c *current) onNode266() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode241() (any, error) { +func (p *parser) callonNode266() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode241() + return p.cur.onNode266() } -func (c *current) onNode221(v any) (any, error) { +func (c *current) onNode246(v any) (any, error) { return buildStringNode("", v, c.text, c.pos) } -func (p *parser) callonNode221() (any, error) { +func (p *parser) callonNode246() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode221(stack["v"]) + return p.cur.onNode246(stack["v"]) } -func (c *current) onNode246() (any, error) { +func (c *current) onNode271() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode246() (any, error) { +func (p *parser) callonNode271() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode246() + return p.cur.onNode271() } -func (c *current) onNode248() (any, error) { +func (c *current) onNode273() (any, error) { return nil, nil } -func (p *parser) callonNode248() (any, error) { +func (p *parser) callonNode273() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode248() + return p.cur.onNode273() } -func (c *current) onNode254(v any) (any, error) { +func (c *current) onNode279(v any) (any, error) { return nil, nil } -func (p *parser) callonNode254() (any, error) { +func (p *parser) callonNode279() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode254(stack["v"]) + return p.cur.onNode279(stack["v"]) } -func (c *current) onNode258() (any, error) { +func (c *current) onNode283() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode258() (any, error) { +func (p *parser) callonNode283() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode258() + return p.cur.onNode283() } -func (c *current) onNode243(v any) (any, error) { +func (c *current) onNode268(v any) (any, error) { return buildStringNode("", v, c.text, c.pos) } -func (p *parser) callonNode243() (any, error) { +func (p *parser) callonNode268() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode243(stack["v"]) + return p.cur.onNode268(stack["v"]) } func (c *current) onGroupNode6() (any, error) { diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index 7e86456a999..30a91e21f33 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/araddon/dateparse" + "github.com/jinzhu/now" tAssert "github.com/stretchr/testify/assert" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" @@ -14,7 +14,7 @@ import ( ) var mustParseTime = func(t *testing.T, ts string) time.Time { - tp, err := dateparse.ParseLocal(ts) + tp, err := now.Parse(ts) if err != nil { t.Fatalf("time.Parse(...) error = %v", err) } @@ -22,7 +22,19 @@ var mustParseTime = func(t *testing.T, ts string) time.Time { return tp } -var mustJoin = func(v []string) string { +var setWorldClock = func(t *testing.T, ts string) func() func() { + return func() func() { + kql.PatchTimeNow(func() time.Time { + return mustParseTime(t, ts) + }) + + return func() { + kql.PatchTimeNow(time.Now) + } + } +} + +var join = func(v []string) string { return strings.Join(v, " ") } @@ -60,6 +72,7 @@ func TestParse(t *testing.T) { givenQuery string expectedAst *ast.Ast expectedError error + prepare func() func() }{ // SPEC ////////////////////////////////////////////////////////////////////////////// // @@ -451,14 +464,29 @@ func TestParse(t *testing.T) { }, }, { - name: `Modified:today`, - skip: true, + name: `Modified:today`, + prepare: setWorldClock(t, "2023-09-10"), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, + }, }, ////////////////////////////////////////////////////////////////////////////////////// // everything else { name: "FullDictionary", - givenQuery: mustJoin(FullDictionary), + givenQuery: join(FullDictionary), expectedAst: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "federated"}, @@ -566,7 +594,7 @@ func TestParse(t *testing.T) { }, { name: "Group", - givenQuery: mustJoin([]string{ + givenQuery: join([]string{ `(name:"moby di*" OR tag:bestseller) AND tag:book NOT tag:read`, `author:("John Smith" Jane)`, `author:("John Smith" OR Jane)`, @@ -712,7 +740,7 @@ func TestParse(t *testing.T) { }, { name: "DateTimeRestrictionNode", - givenQuery: mustJoin([]string{ + givenQuery: join([]string{ `Mtime:"2023-09-05T08:42:11.23554+02:00"`, `Mtime:2023-09-05T08:42:11.23554+02:00`, `Mtime="2023-09-05T08:42:11.23554+02:00"`, @@ -802,9 +830,207 @@ func TestParse(t *testing.T) { }, }, }, + { + name: "NaturalLanguage DateTimeNode - today", + prepare: setWorldClock(t, "2023-09-10"), + givenQuery: join([]string{ + `Mtime:today`, + }), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - yesterday", + prepare: setWorldClock(t, "2023-09-10"), + givenQuery: join([]string{ + `Mtime:yesterday`, + }), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-09"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-09 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - yesterday - the beginning of the month", + prepare: setWorldClock(t, "2023-09-01"), + givenQuery: join([]string{ + `Mtime:yesterday`, + }), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-31"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this week", + prepare: setWorldClock(t, "2023-09-06"), + givenQuery: join([]string{ + `Mtime:"this week"`, + }), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-04"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this month", + prepare: setWorldClock(t, "2023-09-02"), + givenQuery: join([]string{ + `Mtime:"this month"`, + }), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-30 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last month", + prepare: setWorldClock(t, "2023-09-02"), + givenQuery: join([]string{ + `Mtime:"last month"`, + }), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last month - the beginning of the year", + prepare: setWorldClock(t, "2023-01-01"), + givenQuery: join([]string{ + `Mtime:"last month"`, + }), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-12-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this year", + prepare: setWorldClock(t, "2023-06-18"), + givenQuery: join([]string{ + `Mtime:"this year"`, + }), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-12-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last year", + prepare: setWorldClock(t, "2023-01-01"), + givenQuery: join([]string{ + `Mtime:"last year"`, + }), + expectedAst: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + }, + }, + }, + }, { name: "id", - givenQuery: mustJoin([]string{ + givenQuery: join([]string{ `id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, `ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, }), @@ -1027,6 +1253,11 @@ func TestParse(t *testing.T) { t.Skip() } + if tt.prepare != nil { + prepare := tt.prepare() + defer prepare() + } + q := tt.name if tt.givenQuery != "" { diff --git a/services/search/pkg/query/kql/doc.go b/services/search/pkg/query/kql/doc.go index a887affc32f..f578d171f2f 100644 --- a/services/search/pkg/query/kql/doc.go +++ b/services/search/pkg/query/kql/doc.go @@ -10,7 +10,6 @@ The following spec parts are supported and tested: - 2.1.8 OR Operator - 2.1.12 Parentheses - 2.3.5 Date Tokens - - Human tokens not implemented - 3.1.11 Implicit Operator - 3.1.12 Parentheses - 3.1.2 AND Operator diff --git a/services/search/pkg/query/kql/engine_suite_test.go b/services/search/pkg/query/kql/engine_suite_test.go new file mode 100644 index 00000000000..44a37b93179 --- /dev/null +++ b/services/search/pkg/query/kql/engine_suite_test.go @@ -0,0 +1,11 @@ +package kql + +import ( + "time" +) + +// PatchTimeNow is here to path the package time now func, +// this only exists for the tests context +func PatchTimeNow(t func() time.Time) { + timeNow = t +} diff --git a/services/search/pkg/query/kql/error.go b/services/search/pkg/query/kql/error.go index c12cbfe9d00..f285c0a8301 100644 --- a/services/search/pkg/query/kql/error.go +++ b/services/search/pkg/query/kql/error.go @@ -28,3 +28,15 @@ func (e NamedGroupInvalidNodesError) Error() string { ast.NodeValue(e.Node), ).Error() } + +// UnsupportedTimeRange records an error and the value that caused it. +type UnsupportedTimeRange struct { + Value interface{} +} + +func (e UnsupportedTimeRange) Error() string { + return fmt.Errorf( + "unable to convert '%v' to a time range", + e.Value, + ).Error() +} diff --git a/services/search/pkg/query/kql/factory.go b/services/search/pkg/query/kql/factory.go index ff9044886a9..0eb858dd97a 100644 --- a/services/search/pkg/query/kql/factory.go +++ b/services/search/pkg/query/kql/factory.go @@ -101,6 +101,39 @@ func buildDateTimeNode(k, o, v interface{}, text []byte, pos position) (*ast.Dat Value: value, }, nil } +func buildNaturalLanguageDateTimeNodes(k, v interface{}, text []byte, pos position) ([]ast.Node, error) { + b, err := base(text, pos) + if err != nil { + return nil, err + } + + key, err := toString(k) + if err != nil { + return nil, err + } + + from, to, err := toTimeRange(v) + if err != nil { + return nil, err + } + + return []ast.Node{ + &ast.DateTimeNode{ + Base: b, + Value: *from, + Key: key, + Operator: &ast.OperatorNode{Value: ">="}, + }, + &ast.OperatorNode{Value: BoolAND}, + &ast.DateTimeNode{ + Base: b, + Value: *to, + Key: key, + Operator: &ast.OperatorNode{Value: "<="}, + }, + }, nil + +} func buildBooleanNode(k, v interface{}, text []byte, pos position) (*ast.BooleanNode, error) { b, err := base(text, pos) diff --git a/services/search/pkg/query/kql/kql.go b/services/search/pkg/query/kql/kql.go index 8719223b8d2..2cf03a7f066 100644 --- a/services/search/pkg/query/kql/kql.go +++ b/services/search/pkg/query/kql/kql.go @@ -3,6 +3,7 @@ package kql import ( "errors" + "time" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -42,3 +43,7 @@ func (b Builder) Build(q string) (*ast.Ast, error) { return f.(*ast.Ast), nil } + +// timeNow mirrors time.Now by default, the only reason why this exists +// is to monkey patch it from the tests. See PatchTimeNow +var timeNow = time.Now diff --git a/vendor/github.com/araddon/dateparse/.travis.yml b/vendor/github.com/araddon/dateparse/.travis.yml deleted file mode 100644 index 3b4b17777fb..00000000000 --- a/vendor/github.com/araddon/dateparse/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: go - -go: - - 1.13.x - -before_install: - - go get -t -v ./... - -script: - - go test -race -coverprofile=coverage.txt -covermode=atomic - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/araddon/dateparse/README.md b/vendor/github.com/araddon/dateparse/README.md deleted file mode 100644 index fe682dd5652..00000000000 --- a/vendor/github.com/araddon/dateparse/README.md +++ /dev/null @@ -1,323 +0,0 @@ -Go Date Parser ---------------------------- - -Parse many date strings without knowing format in advance. Uses a scanner to read bytes and use a state machine to find format. Much faster than shotgun based parse methods. See [bench_test.go](https://github.com/araddon/dateparse/blob/master/bench_test.go) for performance comparison. - - -[![Code Coverage](https://codecov.io/gh/araddon/dateparse/branch/master/graph/badge.svg)](https://codecov.io/gh/araddon/dateparse) -[![GoDoc](https://godoc.org/github.com/araddon/dateparse?status.svg)](http://godoc.org/github.com/araddon/dateparse) -[![Build Status](https://travis-ci.org/araddon/dateparse.svg?branch=master)](https://travis-ci.org/araddon/dateparse) -[![Go ReportCard](https://goreportcard.com/badge/araddon/dateparse)](https://goreportcard.com/report/araddon/dateparse) - -**MM/DD/YYYY VS DD/MM/YYYY** Right now this uses mm/dd/yyyy WHEN ambiguous if this is not desired behavior, use `ParseStrict` which will fail on ambiguous date strings. - -**Timezones** The location your server is configured affects the results! See example or https://play.golang.org/p/IDHRalIyXh and last paragraph here https://golang.org/pkg/time/#Parse. - - -```go - -// Normal parse. Equivalent Timezone rules as time.Parse() -t, err := dateparse.ParseAny("3/1/2014") - -// Parse Strict, error on ambigous mm/dd vs dd/mm dates -t, err := dateparse.ParseStrict("3/1/2014") -> returns error - -// Return a string that represents the layout to parse the given date-time. -layout, err := dateparse.ParseFormat("May 8, 2009 5:57:51 PM") -> "Jan 2, 2006 3:04:05 PM" - -``` - -cli tool for testing dateformats ----------------------------------- - -[Date Parse CLI](https://github.com/araddon/dateparse/blob/master/dateparse) - - -Extended example -------------------- - -https://github.com/araddon/dateparse/blob/master/example/main.go - -```go -package main - -import ( - "flag" - "fmt" - "time" - - "github.com/scylladb/termtables" - "github.com/araddon/dateparse" -) - -var examples = []string{ - "May 8, 2009 5:57:51 PM", - "oct 7, 1970", - "oct 7, '70", - "oct. 7, 1970", - "oct. 7, 70", - "Mon Jan 2 15:04:05 2006", - "Mon Jan 2 15:04:05 MST 2006", - "Mon Jan 02 15:04:05 -0700 2006", - "Monday, 02-Jan-06 15:04:05 MST", - "Mon, 02 Jan 2006 15:04:05 MST", - "Tue, 11 Jul 2017 16:28:13 +0200 (CEST)", - "Mon, 02 Jan 2006 15:04:05 -0700", - "Mon 30 Sep 2018 09:09:09 PM UTC", - "Mon Aug 10 15:44:11 UTC+0100 2015", - "Thu, 4 Jan 2018 17:53:36 +0000", - "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)", - "Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00)", - "September 17, 2012 10:09am", - "September 17, 2012 at 10:09am PST-08", - "September 17, 2012, 10:10:09", - "October 7, 1970", - "October 7th, 1970", - "12 Feb 2006, 19:17", - "12 Feb 2006 19:17", - "14 May 2019 19:11:40.164", - "7 oct 70", - "7 oct 1970", - "03 February 2013", - "1 July 2013", - "2013-Feb-03", - // dd/Mon/yyy alpha Months - "06/Jan/2008:15:04:05 -0700", - "06/Jan/2008 15:04:05 -0700", - // mm/dd/yy - "3/31/2014", - "03/31/2014", - "08/21/71", - "8/1/71", - "4/8/2014 22:05", - "04/08/2014 22:05", - "4/8/14 22:05", - "04/2/2014 03:00:51", - "8/8/1965 12:00:00 AM", - "8/8/1965 01:00:01 PM", - "8/8/1965 01:00 PM", - "8/8/1965 1:00 PM", - "8/8/1965 12:00 AM", - "4/02/2014 03:00:51", - "03/19/2012 10:11:59", - "03/19/2012 10:11:59.3186369", - // yyyy/mm/dd - "2014/3/31", - "2014/03/31", - "2014/4/8 22:05", - "2014/04/08 22:05", - "2014/04/2 03:00:51", - "2014/4/02 03:00:51", - "2012/03/19 10:11:59", - "2012/03/19 10:11:59.3186369", - // yyyy:mm:dd - "2014:3:31", - "2014:03:31", - "2014:4:8 22:05", - "2014:04:08 22:05", - "2014:04:2 03:00:51", - "2014:4:02 03:00:51", - "2012:03:19 10:11:59", - "2012:03:19 10:11:59.3186369", - // Chinese - "2014ๅนด04ๆœˆ08ๆ—ฅ", - // yyyy-mm-ddThh - "2006-01-02T15:04:05+0000", - "2009-08-12T22:15:09-07:00", - "2009-08-12T22:15:09", - "2009-08-12T22:15:09.988", - "2009-08-12T22:15:09Z", - "2017-07-19T03:21:51:897+0100", - "2019-05-29T08:41-04", // no seconds, 2 digit TZ offset - // yyyy-mm-dd hh:mm:ss - "2014-04-26 17:24:37.3186369", - "2012-08-03 18:31:59.257000000", - "2014-04-26 17:24:37.123", - "2013-04-01 22:43", - "2013-04-01 22:43:22", - "2014-12-16 06:20:00 UTC", - "2014-12-16 06:20:00 GMT", - "2014-04-26 05:24:37 PM", - "2014-04-26 13:13:43 +0800", - "2014-04-26 13:13:43 +0800 +08", - "2014-04-26 13:13:44 +09:00", - "2012-08-03 18:31:59.257000000 +0000 UTC", - "2015-09-30 18:48:56.35272715 +0000 UTC", - "2015-02-18 00:12:00 +0000 GMT", - "2015-02-18 00:12:00 +0000 UTC", - "2015-02-08 03:02:00 +0300 MSK m=+0.000000001", - "2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001", - "2017-07-19 03:21:51+00:00", - "2014-04-26", - "2014-04", - "2014", - "2014-05-11 08:20:13,787", - // yyyy-mm-dd-07:00 - "2020-07-20+08:00", - // mm.dd.yy - "3.31.2014", - "03.31.2014", - "08.21.71", - "2014.03", - "2014.03.30", - // yyyymmdd and similar - "20140601", - "20140722105203", - // yymmdd hh:mm:yy mysql log - // 080313 05:21:55 mysqld started - "171113 14:14:20", - // unix seconds, ms, micro, nano - "1332151919", - "1384216367189", - "1384216367111222", - "1384216367111222333", -} - -var ( - timezone = "" -) - -func main() { - flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone") - flag.Parse() - - if timezone != "" { - // NOTE: This is very, very important to understand - // time-parsing in go - loc, err := time.LoadLocation(timezone) - if err != nil { - panic(err.Error()) - } - time.Local = loc - } - - table := termtables.CreateTable() - - table.AddHeaders("Input", "Parsed, and Output as %v") - for _, dateExample := range examples { - t, err := dateparse.ParseLocal(dateExample) - if err != nil { - panic(err.Error()) - } - table.AddRow(dateExample, fmt.Sprintf("%v", t)) - } - fmt.Println(table.Render()) -} - -/* -+-------------------------------------------------------+-----------------------------------------+ -| Input | Parsed, and Output as %v | -+-------------------------------------------------------+-----------------------------------------+ -| May 8, 2009 5:57:51 PM | 2009-05-08 17:57:51 +0000 UTC | -| oct 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| oct 7, '70 | 1970-10-07 00:00:00 +0000 UTC | -| oct. 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| oct. 7, 70 | 1970-10-07 00:00:00 +0000 UTC | -| Mon Jan 2 15:04:05 2006 | 2006-01-02 15:04:05 +0000 UTC | -| Mon Jan 2 15:04:05 MST 2006 | 2006-01-02 15:04:05 +0000 MST | -| Mon Jan 02 15:04:05 -0700 2006 | 2006-01-02 15:04:05 -0700 -0700 | -| Monday, 02-Jan-06 15:04:05 MST | 2006-01-02 15:04:05 +0000 MST | -| Mon, 02 Jan 2006 15:04:05 MST | 2006-01-02 15:04:05 +0000 MST | -| Tue, 11 Jul 2017 16:28:13 +0200 (CEST) | 2017-07-11 16:28:13 +0200 +0200 | -| Mon, 02 Jan 2006 15:04:05 -0700 | 2006-01-02 15:04:05 -0700 -0700 | -| Mon 30 Sep 2018 09:09:09 PM UTC | 2018-09-30 21:09:09 +0000 UTC | -| Mon Aug 10 15:44:11 UTC+0100 2015 | 2015-08-10 15:44:11 +0000 UTC | -| Thu, 4 Jan 2018 17:53:36 +0000 | 2018-01-04 17:53:36 +0000 UTC | -| Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 GMT | -| Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00) | 2021-01-03 00:12:23 +0800 +0800 | -| September 17, 2012 10:09am | 2012-09-17 10:09:00 +0000 UTC | -| September 17, 2012 at 10:09am PST-08 | 2012-09-17 10:09:00 -0800 PST | -| September 17, 2012, 10:10:09 | 2012-09-17 10:10:09 +0000 UTC | -| October 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| October 7th, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| 12 Feb 2006, 19:17 | 2006-02-12 19:17:00 +0000 UTC | -| 12 Feb 2006 19:17 | 2006-02-12 19:17:00 +0000 UTC | -| 14 May 2019 19:11:40.164 | 2019-05-14 19:11:40.164 +0000 UTC | -| 7 oct 70 | 1970-10-07 00:00:00 +0000 UTC | -| 7 oct 1970 | 1970-10-07 00:00:00 +0000 UTC | -| 03 February 2013 | 2013-02-03 00:00:00 +0000 UTC | -| 1 July 2013 | 2013-07-01 00:00:00 +0000 UTC | -| 2013-Feb-03 | 2013-02-03 00:00:00 +0000 UTC | -| 06/Jan/2008:15:04:05 -0700 | 2008-01-06 15:04:05 -0700 -0700 | -| 06/Jan/2008 15:04:05 -0700 | 2008-01-06 15:04:05 -0700 -0700 | -| 3/31/2014 | 2014-03-31 00:00:00 +0000 UTC | -| 03/31/2014 | 2014-03-31 00:00:00 +0000 UTC | -| 08/21/71 | 1971-08-21 00:00:00 +0000 UTC | -| 8/1/71 | 1971-08-01 00:00:00 +0000 UTC | -| 4/8/2014 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 04/08/2014 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 4/8/14 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 04/2/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 8/8/1965 12:00:00 AM | 1965-08-08 00:00:00 +0000 UTC | -| 8/8/1965 01:00:01 PM | 1965-08-08 13:00:01 +0000 UTC | -| 8/8/1965 01:00 PM | 1965-08-08 13:00:00 +0000 UTC | -| 8/8/1965 1:00 PM | 1965-08-08 13:00:00 +0000 UTC | -| 8/8/1965 12:00 AM | 1965-08-08 00:00:00 +0000 UTC | -| 4/02/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 03/19/2012 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | -| 03/19/2012 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | -| 2014/3/31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014/03/31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014/4/8 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014/04/08 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014/04/2 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2014/4/02 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2012/03/19 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | -| 2012/03/19 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | -| 2014:3:31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014:03:31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014:4:8 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014:04:08 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014:04:2 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2014:4:02 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2012:03:19 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | -| 2012:03:19 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | -| 2014ๅนด04ๆœˆ08ๆ—ฅ | 2014-04-08 00:00:00 +0000 UTC | -| 2006-01-02T15:04:05+0000 | 2006-01-02 15:04:05 +0000 UTC | -| 2009-08-12T22:15:09-07:00 | 2009-08-12 22:15:09 -0700 -0700 | -| 2009-08-12T22:15:09 | 2009-08-12 22:15:09 +0000 UTC | -| 2009-08-12T22:15:09.988 | 2009-08-12 22:15:09.988 +0000 UTC | -| 2009-08-12T22:15:09Z | 2009-08-12 22:15:09 +0000 UTC | -| 2017-07-19T03:21:51:897+0100 | 2017-07-19 03:21:51.897 +0100 +0100 | -| 2019-05-29T08:41-04 | 2019-05-29 08:41:00 -0400 -0400 | -| 2014-04-26 17:24:37.3186369 | 2014-04-26 17:24:37.3186369 +0000 UTC | -| 2012-08-03 18:31:59.257000000 | 2012-08-03 18:31:59.257 +0000 UTC | -| 2014-04-26 17:24:37.123 | 2014-04-26 17:24:37.123 +0000 UTC | -| 2013-04-01 22:43 | 2013-04-01 22:43:00 +0000 UTC | -| 2013-04-01 22:43:22 | 2013-04-01 22:43:22 +0000 UTC | -| 2014-12-16 06:20:00 UTC | 2014-12-16 06:20:00 +0000 UTC | -| 2014-12-16 06:20:00 GMT | 2014-12-16 06:20:00 +0000 UTC | -| 2014-04-26 05:24:37 PM | 2014-04-26 17:24:37 +0000 UTC | -| 2014-04-26 13:13:43 +0800 | 2014-04-26 13:13:43 +0800 +0800 | -| 2014-04-26 13:13:43 +0800 +08 | 2014-04-26 13:13:43 +0800 +0800 | -| 2014-04-26 13:13:44 +09:00 | 2014-04-26 13:13:44 +0900 +0900 | -| 2012-08-03 18:31:59.257000000 +0000 UTC | 2012-08-03 18:31:59.257 +0000 UTC | -| 2015-09-30 18:48:56.35272715 +0000 UTC | 2015-09-30 18:48:56.35272715 +0000 UTC | -| 2015-02-18 00:12:00 +0000 GMT | 2015-02-18 00:12:00 +0000 UTC | -| 2015-02-18 00:12:00 +0000 UTC | 2015-02-18 00:12:00 +0000 UTC | -| 2015-02-08 03:02:00 +0300 MSK m=+0.000000001 | 2015-02-08 03:02:00 +0300 +0300 | -| 2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001 | 2015-02-08 03:02:00.001 +0300 +0300 | -| 2017-07-19 03:21:51+00:00 | 2017-07-19 03:21:51 +0000 UTC | -| 2014-04-26 | 2014-04-26 00:00:00 +0000 UTC | -| 2014-04 | 2014-04-01 00:00:00 +0000 UTC | -| 2014 | 2014-01-01 00:00:00 +0000 UTC | -| 2014-05-11 08:20:13,787 | 2014-05-11 08:20:13.787 +0000 UTC | -| 2020-07-20+08:00 | 2020-07-20 00:00:00 +0800 +0800 | -| 3.31.2014 | 2014-03-31 00:00:00 +0000 UTC | -| 03.31.2014 | 2014-03-31 00:00:00 +0000 UTC | -| 08.21.71 | 1971-08-21 00:00:00 +0000 UTC | -| 2014.03 | 2014-03-01 00:00:00 +0000 UTC | -| 2014.03.30 | 2014-03-30 00:00:00 +0000 UTC | -| 20140601 | 2014-06-01 00:00:00 +0000 UTC | -| 20140722105203 | 2014-07-22 10:52:03 +0000 UTC | -| 171113 14:14:20 | 2017-11-13 14:14:20 +0000 UTC | -| 1332151919 | 2012-03-19 10:11:59 +0000 UTC | -| 1384216367189 | 2013-11-12 00:32:47.189 +0000 UTC | -| 1384216367111222 | 2013-11-12 00:32:47.111222 +0000 UTC | -| 1384216367111222333 | 2013-11-12 00:32:47.111222333 +0000 UTC | -+-------------------------------------------------------+-----------------------------------------+ -*/ - -``` diff --git a/vendor/github.com/araddon/dateparse/parseany.go b/vendor/github.com/araddon/dateparse/parseany.go deleted file mode 100644 index b9668b21bd4..00000000000 --- a/vendor/github.com/araddon/dateparse/parseany.go +++ /dev/null @@ -1,2189 +0,0 @@ -// Package dateparse parses date-strings without knowing the format -// in advance, using a fast lex based approach to eliminate shotgun -// attempts. It leans towards US style dates when there is a conflict. -package dateparse - -import ( - "fmt" - "strconv" - "strings" - "time" - "unicode" - "unicode/utf8" -) - -// func init() { -// gou.SetupLogging("debug") -// gou.SetColorOutput() -// } - -var days = []string{ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun", - "monday", - "tuesday", - "wednesday", - "thursday", - "friday", - "saturday", - "sunday", -} - -var months = []string{ - "january", - "february", - "march", - "april", - "may", - "june", - "july", - "august", - "september", - "october", - "november", - "december", -} - -type dateState uint8 -type timeState uint8 - -const ( - dateStart dateState = iota // 0 - dateDigit - dateDigitSt - dateYearDash - dateYearDashAlphaDash - dateYearDashDash - dateYearDashDashWs // 5 - dateYearDashDashT - dateYearDashDashOffset - dateDigitDash - dateDigitDashAlpha - dateDigitDashAlphaDash // 10 - dateDigitDot - dateDigitDotDot - dateDigitSlash - dateDigitYearSlash - dateDigitSlashAlpha // 15 - dateDigitColon - dateDigitChineseYear - dateDigitChineseYearWs - dateDigitWs - dateDigitWsMoYear // 20 - dateDigitWsMolong - dateAlpha - dateAlphaWs - dateAlphaWsDigit - dateAlphaWsDigitMore // 25 - dateAlphaWsDigitMoreWs - dateAlphaWsDigitMoreWsYear - dateAlphaWsMonth - dateAlphaWsDigitYearmaybe - dateAlphaWsMonthMore - dateAlphaWsMonthSuffix - dateAlphaWsMore - dateAlphaWsAtTime - dateAlphaWsAlpha - dateAlphaWsAlphaYearmaybe // 35 - dateAlphaPeriodWsDigit - dateWeekdayComma - dateWeekdayAbbrevComma -) -const ( - // Time state - timeIgnore timeState = iota // 0 - timeStart - timeWs - timeWsAlpha - timeWsAlphaWs - timeWsAlphaZoneOffset // 5 - timeWsAlphaZoneOffsetWs - timeWsAlphaZoneOffsetWsYear - timeWsAlphaZoneOffsetWsExtra - timeWsAMPMMaybe - timeWsAMPM // 10 - timeWsOffset - timeWsOffsetWs // 12 - timeWsOffsetColonAlpha - timeWsOffsetColon - timeWsYear // 15 - timeOffset - timeOffsetColon - timeAlpha - timePeriod - timePeriodOffset // 20 - timePeriodOffsetColon - timePeriodOffsetColonWs - timePeriodWs - timePeriodWsAlpha - timePeriodWsOffset // 25 - timePeriodWsOffsetWs - timePeriodWsOffsetWsAlpha - timePeriodWsOffsetColon - timePeriodWsOffsetColonAlpha - timeZ - timeZDigit -) - -var ( - // ErrAmbiguousMMDD for date formats such as 04/02/2014 the mm/dd vs dd/mm are - // ambiguous, so it is an error for strict parse rules. - ErrAmbiguousMMDD = fmt.Errorf("This date has ambiguous mm/dd vs dd/mm type format") -) - -func unknownErr(datestr string) error { - return fmt.Errorf("Could not find format for %q", datestr) -} - -// ParseAny parse an unknown date format, detect the layout. -// Normal parse. Equivalent Timezone rules as time.Parse(). -// NOTE: please see readme on mmdd vs ddmm ambiguous dates. -func ParseAny(datestr string, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - return time.Time{}, err - } - return p.parse() -} - -// ParseIn with Location, equivalent to time.ParseInLocation() timezone/offset -// rules. Using location arg, if timezone/offset info exists in the -// datestring, it uses the given location rules for any zone interpretation. -// That is, MST means one thing when using America/Denver and something else -// in other locations. -func ParseIn(datestr string, loc *time.Location, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, loc, opts...) - if err != nil { - return time.Time{}, err - } - return p.parse() -} - -// ParseLocal Given an unknown date format, detect the layout, -// using time.Local, parse. -// -// Set Location to time.Local. Same as ParseIn Location but lazily uses -// the global time.Local variable for Location argument. -// -// denverLoc, _ := time.LoadLocation("America/Denver") -// time.Local = denverLoc -// -// t, err := dateparse.ParseLocal("3/1/2014") -// -// Equivalent to: -// -// t, err := dateparse.ParseIn("3/1/2014", denverLoc) -// -func ParseLocal(datestr string, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, time.Local, opts...) - if err != nil { - return time.Time{}, err - } - return p.parse() -} - -// MustParse parse a date, and panic if it can't be parsed. Used for testing. -// Not recommended for most use-cases. -func MustParse(datestr string, opts ...ParserOption) time.Time { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - panic(err.Error()) - } - t, err := p.parse() - if err != nil { - panic(err.Error()) - } - return t -} - -// ParseFormat parse's an unknown date-time string and returns a layout -// string that can parse this (and exact same format) other date-time strings. -// -// layout, err := dateparse.ParseFormat("2013-02-01 00:00:00") -// // layout = "2006-01-02 15:04:05" -// -func ParseFormat(datestr string, opts ...ParserOption) (string, error) { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - return "", err - } - _, err = p.parse() - if err != nil { - return "", err - } - return string(p.format), nil -} - -// ParseStrict parse an unknown date format. IF the date is ambigous -// mm/dd vs dd/mm then return an error. These return errors: 3.3.2014 , 8/8/71 etc -func ParseStrict(datestr string, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - return time.Time{}, err - } - if p.ambiguousMD { - return time.Time{}, ErrAmbiguousMMDD - } - return p.parse() -} - -func parseTime(datestr string, loc *time.Location, opts ...ParserOption) (p *parser, err error) { - - p = newParser(datestr, loc, opts...) - if p.retryAmbiguousDateWithSwap { - // month out of range signifies that a day/month swap is the correct solution to an ambiguous date - // this is because it means that a day is being interpreted as a month and overflowing the valid value for that - // by retrying in this case, we can fix a common situation with no assumptions - defer func() { - if p != nil && p.ambiguousMD { - // if it errors out with the following error, swap before we - // get out of this function to reduce scope it needs to be applied on - _, err := p.parse() - if err != nil && strings.Contains(err.Error(), "month out of range") { - // create the option to reverse the preference - preferMonthFirst := PreferMonthFirst(!p.preferMonthFirst) - // turn off the retry to avoid endless recursion - retryAmbiguousDateWithSwap := RetryAmbiguousDateWithSwap(false) - modifiedOpts := append(opts, preferMonthFirst, retryAmbiguousDateWithSwap) - p, err = parseTime(datestr, time.Local, modifiedOpts...) - } - } - - }() - } - - i := 0 - - // General strategy is to read rune by rune through the date looking for - // certain hints of what type of date we are dealing with. - // Hopefully we only need to read about 5 or 6 bytes before - // we figure it out and then attempt a parse -iterRunes: - for ; i < len(datestr); i++ { - //r := rune(datestr[i]) - r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:]) - if bytesConsumed > 1 { - i += (bytesConsumed - 1) - } - - // gou.Debugf("i=%d r=%s state=%d %s", i, string(r), p.stateDate, datestr) - switch p.stateDate { - case dateStart: - if unicode.IsDigit(r) { - p.stateDate = dateDigit - } else if unicode.IsLetter(r) { - p.stateDate = dateAlpha - } else { - return nil, unknownErr(datestr) - } - case dateDigit: - - switch r { - case '-', '\u2212': - // 2006-01-02 - // 2013-Feb-03 - // 13-Feb-03 - // 29-Jun-2016 - if i == 4 { - p.stateDate = dateYearDash - p.yeari = 0 - p.yearlen = i - p.moi = i + 1 - p.set(0, "2006") - } else { - p.stateDate = dateDigitDash - } - case '/': - // 08/May/2005 - // 03/31/2005 - // 2014/02/24 - p.stateDate = dateDigitSlash - if i == 4 { - // 2014/02/24 - Year first / - p.yearlen = i // since it was start of datestr, i=len - p.moi = i + 1 - p.setYear() - p.stateDate = dateDigitYearSlash - } else { - // Either Ambiguous dd/mm vs mm/dd OR dd/month/yy - // 08/May/2005 - // 03/31/2005 - // 31/03/2005 - if i+2 < len(p.datestr) && unicode.IsLetter(rune(datestr[i+1])) { - // 08/May/2005 - p.stateDate = dateDigitSlashAlpha - p.moi = i + 1 - p.daylen = 2 - p.dayi = 0 - p.setDay() - continue - } - // Ambiguous dd/mm vs mm/dd the bane of date-parsing - // 03/31/2005 - // 31/03/2005 - p.ambiguousMD = true - if p.preferMonthFirst { - if p.molen == 0 { - // 03/31/2005 - p.molen = i - p.setMonth() - p.dayi = i + 1 - } - } else { - if p.daylen == 0 { - p.daylen = i - p.setDay() - p.moi = i + 1 - } - } - - } - - case ':': - // 03/31/2005 - // 2014/02/24 - p.stateDate = dateDigitColon - if i == 4 { - p.yearlen = i - p.moi = i + 1 - p.setYear() - } else { - p.ambiguousMD = true - if p.preferMonthFirst { - if p.molen == 0 { - p.molen = i - p.setMonth() - p.dayi = i + 1 - } - } - } - - case '.': - // 3.31.2014 - // 08.21.71 - // 2014.05 - p.stateDate = dateDigitDot - if i == 4 { - p.yearlen = i - p.moi = i + 1 - p.setYear() - } else { - p.ambiguousMD = true - p.moi = 0 - p.molen = i - p.setMonth() - p.dayi = i + 1 - } - - case ' ': - // 18 January 2018 - // 8 January 2018 - // 8 jan 2018 - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - if i == 6 { - p.stateDate = dateDigitSt - } else { - p.stateDate = dateDigitWs - p.dayi = 0 - p.daylen = i - } - case 'ๅนด': - // Chinese Year - p.stateDate = dateDigitChineseYear - case ',': - return nil, unknownErr(datestr) - default: - continue - } - p.part1Len = i - - case dateDigitSt: - p.set(0, "060102") - i = i - 1 - p.stateTime = timeStart - break iterRunes - case dateYearDash: - // dateYearDashDashT - // 2006-01-02T15:04:05Z07:00 - // 2020-08-17T17:00:00:000+0100 - // dateYearDashDashWs - // 2013-04-01 22:43:22 - // dateYearDashAlphaDash - // 2013-Feb-03 - switch r { - case '-': - p.molen = i - p.moi - p.dayi = i + 1 - p.stateDate = dateYearDashDash - p.setMonth() - default: - if unicode.IsLetter(r) { - p.stateDate = dateYearDashAlphaDash - } - } - - case dateYearDashDash: - // dateYearDashDashT - // 2006-01-02T15:04:05Z07:00 - // dateYearDashDashWs - // 2013-04-01 22:43:22 - // dateYearDashDashOffset - // 2020-07-20+00:00 - switch r { - case '+', '-': - p.offseti = i - p.daylen = i - p.dayi - p.stateDate = dateYearDashDashOffset - p.setDay() - case ' ': - p.daylen = i - p.dayi - p.stateDate = dateYearDashDashWs - p.stateTime = timeStart - p.setDay() - break iterRunes - case 'T': - p.daylen = i - p.dayi - p.stateDate = dateYearDashDashT - p.stateTime = timeStart - p.setDay() - break iterRunes - } - - case dateYearDashDashT: - // dateYearDashDashT - // 2006-01-02T15:04:05Z07:00 - // 2020-08-17T17:00:00:000+0100 - - case dateYearDashDashOffset: - // 2020-07-20+00:00 - switch r { - case ':': - p.set(p.offseti, "-07:00") - // case ' ': - // return nil, unknownErr(datestr) - } - - case dateYearDashAlphaDash: - // 2013-Feb-03 - switch r { - case '-': - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.dayi = i + 1 - } - case dateDigitDash: - // 13-Feb-03 - // 29-Jun-2016 - if unicode.IsLetter(r) { - p.stateDate = dateDigitDashAlpha - p.moi = i - } else { - return nil, unknownErr(datestr) - } - case dateDigitDashAlpha: - // 13-Feb-03 - // 28-Feb-03 - // 29-Jun-2016 - switch r { - case '-': - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.yeari = i + 1 - p.stateDate = dateDigitDashAlphaDash - } - - case dateDigitDashAlphaDash: - // 13-Feb-03 ambiguous - // 28-Feb-03 ambiguous - // 29-Jun-2016 dd-month(alpha)-yyyy - switch r { - case ' ': - // we need to find if this was 4 digits, aka year - // or 2 digits which makes it ambiguous year/day - length := i - (p.moi + p.molen + 1) - if length == 4 { - p.yearlen = 4 - p.set(p.yeari, "2006") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } else if length == 2 { - // We have no idea if this is - // yy-mon-dd OR dd-mon-yy - // - // We are going to ASSUME (bad, bad) that it is dd-mon-yy which is a horible assumption - p.ambiguousMD = true - p.yearlen = 2 - p.set(p.yeari, "06") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } - p.stateTime = timeStart - break iterRunes - } - - case dateDigitYearSlash: - // 2014/07/10 06:55:38.156283 - // I honestly don't know if this format ever shows up as yyyy/ - - switch r { - case ' ', ':': - p.stateTime = timeStart - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - } - break iterRunes - case '/': - if p.molen == 0 { - p.molen = i - p.moi - p.setMonth() - p.dayi = i + 1 - } - } - - case dateDigitSlashAlpha: - // 06/May/2008 - - switch r { - case '/': - // | - // 06/May/2008 - if p.molen == 0 { - p.set(p.moi, "Jan") - p.yeari = i + 1 - } - // We aren't breaking because we are going to re-use this case - // to find where the date starts, and possible time begins - case ' ', ':': - p.stateTime = timeStart - if p.yearlen == 0 { - p.yearlen = i - p.yeari - p.setYear() - } - break iterRunes - } - - case dateDigitSlash: - // 03/19/2012 10:11:59 - // 04/2/2014 03:00:37 - // 3/1/2012 10:11:59 - // 4/8/2014 22:05 - // 3/1/2014 - // 10/13/2014 - // 01/02/2006 - // 1/2/06 - - switch r { - case '/': - // This is the 2nd / so now we should know start pts of all of the dd, mm, yy - if p.preferMonthFirst { - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - } - } else { - if p.molen == 0 { - p.molen = i - p.moi - p.setMonth() - p.yeari = i + 1 - } - } - // Note no break, we are going to pass by and re-enter this dateDigitSlash - // and look for ending (space) or not (just date) - case ' ': - p.stateTime = timeStart - if p.yearlen == 0 { - p.yearlen = i - p.yeari - p.setYear() - } - break iterRunes - } - - case dateDigitColon: - // 2014:07:10 06:55:38.156283 - // 03:19:2012 10:11:59 - // 04:2:2014 03:00:37 - // 3:1:2012 10:11:59 - // 4:8:2014 22:05 - // 3:1:2014 - // 10:13:2014 - // 01:02:2006 - // 1:2:06 - - switch r { - case ' ': - p.stateTime = timeStart - if p.yearlen == 0 { - p.yearlen = i - p.yeari - p.setYear() - } else if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - } - break iterRunes - case ':': - if p.yearlen > 0 { - // 2014:07:10 06:55:38.156283 - if p.molen == 0 { - p.molen = i - p.moi - p.setMonth() - p.dayi = i + 1 - } - } else if p.preferMonthFirst { - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - } - } - } - - case dateDigitWs: - // 18 January 2018 - // 8 January 2018 - // 8 jan 2018 - // 1 jan 18 - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - switch r { - case ' ': - p.yeari = i + 1 - //p.yearlen = 4 - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - p.stateTime = timeStart - if i > p.daylen+len(" Sep") { // November etc - // If len greather than space + 3 it must be full month - p.stateDate = dateDigitWsMolong - } else { - // If len=3, the might be Feb or May? Ie ambigous abbreviated but - // we can parse may with either. BUT, that means the - // format may not be correct? - // mo := strings.ToLower(datestr[p.daylen+1 : i]) - p.moi = p.daylen + 1 - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.stateDate = dateDigitWsMoYear - } - } - - case dateDigitWsMoYear: - // 8 jan 2018 - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - switch r { - case ',': - p.yearlen = i - p.yeari - p.setYear() - i++ - break iterRunes - case ' ': - p.yearlen = i - p.yeari - p.setYear() - break iterRunes - } - case dateDigitWsMolong: - // 18 January 2018 - // 8 January 2018 - - case dateDigitChineseYear: - // dateDigitChineseYear - // 2014ๅนด04ๆœˆ08ๆ—ฅ - // weekday %Yๅนด%mๆœˆ%eๆ—ฅ %A %I:%M %p - // 2013ๅนด07ๆœˆ18ๆ—ฅ ๆ˜ŸๆœŸๅ›› 10:27 ไธŠๅˆ - if r == ' ' { - p.stateDate = dateDigitChineseYearWs - break - } - case dateDigitDot: - // This is the 2nd period - // 3.31.2014 - // 08.21.71 - // 2014.05 - // 2018.09.30 - if r == '.' { - if p.moi == 0 { - // 3.31.2014 - p.daylen = i - p.dayi - p.yeari = i + 1 - p.setDay() - p.stateDate = dateDigitDotDot - } else { - // 2018.09.30 - //p.molen = 2 - p.molen = i - p.moi - p.dayi = i + 1 - p.setMonth() - p.stateDate = dateDigitDotDot - } - } - case dateDigitDotDot: - // iterate all the way through - case dateAlpha: - // dateAlphaWS - // Mon Jan _2 15:04:05 2006 - // Mon Jan _2 15:04:05 MST 2006 - // Mon Jan 02 15:04:05 -0700 2006 - // Mon Aug 10 15:44:11 UTC+0100 2015 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - // dateAlphaWSDigit - // May 8, 2009 5:57:51 PM - // oct 1, 1970 - // dateAlphaWsMonth - // April 8, 2009 - // dateAlphaWsMore - // dateAlphaWsAtTime - // January 02, 2006 at 3:04pm MST-07 - // - // dateAlphaPeriodWsDigit - // oct. 1, 1970 - // dateWeekdayComma - // Monday, 02 Jan 2006 15:04:05 MST - // Monday, 02-Jan-06 15:04:05 MST - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // dateWeekdayAbbrevComma - // Mon, 02 Jan 2006 15:04:05 MST - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - // Mon, 02-Jan-06 15:04:05 MST - switch { - case r == ' ': - // X - // April 8, 2009 - if i > 3 { - // Check to see if the alpha is name of month? or Day? - month := strings.ToLower(datestr[0:i]) - if isMonthFull(month) { - p.fullMonth = month - // len(" 31, 2018") = 9 - if len(datestr[i:]) < 10 { - // April 8, 2009 - p.stateDate = dateAlphaWsMonth - } else { - p.stateDate = dateAlphaWsMore - } - p.dayi = i + 1 - break - } - - } else { - // This is possibly ambiguous? May will parse as either though. - // So, it could return in-correct format. - // dateAlphaWs - // May 05, 2005, 05:05:05 - // May 05 2005, 05:05:05 - // Jul 05, 2005, 05:05:05 - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // skip & return to dateStart - // Tue 05 May 2020, 05:05:05 - // Mon Jan 2 15:04:05 2006 - - maybeDay := strings.ToLower(datestr[0:i]) - if isDay(maybeDay) { - // using skip throws off indices used by other code; saner to restart - return parseTime(datestr[i+1:], loc) - } - p.stateDate = dateAlphaWs - } - - case r == ',': - // Mon, 02 Jan 2006 - - if i == 3 { - p.stateDate = dateWeekdayAbbrevComma - p.set(0, "Mon") - } else { - p.stateDate = dateWeekdayComma - p.skip = i + 2 - i++ - // TODO: lets just make this "skip" as we don't need - // the mon, monday, they are all superfelous and not needed - // just lay down the skip, no need to fill and then skip - } - case r == '.': - // sept. 28, 2017 - // jan. 28, 2017 - p.stateDate = dateAlphaPeriodWsDigit - if i == 3 { - p.molen = i - p.set(0, "Jan") - } else if i == 4 { - // gross - datestr = datestr[0:i-1] + datestr[i:] - return parseTime(datestr, loc, opts...) - } else { - return nil, unknownErr(datestr) - } - } - - case dateAlphaWs: - // dateAlphaWsAlpha - // Mon Jan _2 15:04:05 2006 - // Mon Jan _2 15:04:05 MST 2006 - // Mon Jan 02 15:04:05 -0700 2006 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - // Mon Aug 10 15:44:11 UTC+0100 2015 - // dateAlphaWsDigit - // May 8, 2009 5:57:51 PM - // May 8 2009 5:57:51 PM - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // May 08 17:57:51 2009 - // oct 1, 1970 - // oct 7, '70 - switch { - case unicode.IsLetter(r): - p.set(0, "Mon") - p.stateDate = dateAlphaWsAlpha - p.set(i, "Jan") - case unicode.IsDigit(r): - p.set(0, "Jan") - p.stateDate = dateAlphaWsDigit - p.dayi = i - } - - case dateAlphaWsDigit: - // May 8, 2009 5:57:51 PM - // May 8 2009 5:57:51 PM - // oct 1, 1970 - // oct 7, '70 - // oct. 7, 1970 - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // May 08 17:57:51 2009 - if r == ',' { - p.daylen = i - p.dayi - p.setDay() - p.stateDate = dateAlphaWsDigitMore - } else if r == ' ' { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - p.stateDate = dateAlphaWsDigitYearmaybe - p.stateTime = timeStart - } else if unicode.IsLetter(r) { - p.stateDate = dateAlphaWsMonthSuffix - i-- - } - case dateAlphaWsDigitYearmaybe: - // x - // May 8 2009 5:57:51 PM - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // May 08 17:57:51 2009 - // Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if r == ':' { - // Guessed wrong; was not a year - i = i - 3 - p.stateDate = dateAlphaWsDigit - p.yeari = 0 - break iterRunes - } else if r == ' ' { - // must be year format, not 15:04 - p.yearlen = i - p.yeari - p.setYear() - break iterRunes - } - case dateAlphaWsDigitMore: - // x - // May 8, 2009 5:57:51 PM - // May 05, 2005, 05:05:05 - // May 05 2005, 05:05:05 - // oct 1, 1970 - // oct 7, '70 - if r == ' ' { - p.yeari = i + 1 - p.stateDate = dateAlphaWsDigitMoreWs - } - case dateAlphaWsDigitMoreWs: - // x - // May 8, 2009 5:57:51 PM - // May 05, 2005, 05:05:05 - // oct 1, 1970 - // oct 7, '70 - switch r { - case '\'': - p.yeari = i + 1 - case ' ', ',': - // x - // May 8, 2009 5:57:51 PM - // x - // May 8, 2009, 5:57:51 PM - p.stateDate = dateAlphaWsDigitMoreWsYear - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - break iterRunes - } - - case dateAlphaWsMonth: - // April 8, 2009 - // April 8 2009 - switch r { - case ' ', ',': - // x - // June 8, 2009 - // x - // June 8 2009 - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - } - case 's', 'S', 'r', 'R', 't', 'T', 'n', 'N': - // st, rd, nd, st - i-- - p.stateDate = dateAlphaWsMonthSuffix - default: - if p.daylen > 0 && p.yeari == 0 { - p.yeari = i - } - } - case dateAlphaWsMonthMore: - // X - // January 02, 2006, 15:04:05 - // January 02 2006, 15:04:05 - // January 02, 2006 15:04:05 - // January 02 2006 15:04:05 - switch r { - case ',': - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - i++ - break iterRunes - case ' ': - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - break iterRunes - } - case dateAlphaWsMonthSuffix: - // x - // April 8th, 2009 - // April 8th 2009 - switch r { - case 't', 'T': - if p.nextIs(i, 'h') || p.nextIs(i, 'H') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - case 'n', 'N': - if p.nextIs(i, 'd') || p.nextIs(i, 'D') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - case 's', 'S': - if p.nextIs(i, 't') || p.nextIs(i, 'T') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - case 'r', 'R': - if p.nextIs(i, 'd') || p.nextIs(i, 'D') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - } - case dateAlphaWsMore: - // January 02, 2006, 15:04:05 - // January 02 2006, 15:04:05 - // January 2nd, 2006, 15:04:05 - // January 2nd 2006, 15:04:05 - // September 17, 2012 at 5:00pm UTC-05 - switch { - case r == ',': - // x - // January 02, 2006, 15:04:05 - if p.nextIs(i, ' ') { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 2 - p.stateDate = dateAlphaWsMonthMore - i++ - } - - case r == ' ': - // x - // January 02 2006, 15:04:05 - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - p.stateDate = dateAlphaWsMonthMore - case unicode.IsDigit(r): - // XX - // January 02, 2006, 15:04:05 - continue - case unicode.IsLetter(r): - // X - // January 2nd, 2006, 15:04:05 - p.daylen = i - p.dayi - p.setDay() - p.stateDate = dateAlphaWsMonthSuffix - i-- - } - - case dateAlphaPeriodWsDigit: - // oct. 7, '70 - switch { - case r == ' ': - // continue - case unicode.IsDigit(r): - p.stateDate = dateAlphaWsDigit - p.dayi = i - default: - return p, unknownErr(datestr) - } - case dateWeekdayComma: - // Monday, 02 Jan 2006 15:04:05 MST - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // Monday, 02-Jan-06 15:04:05 MST - if p.dayi == 0 { - p.dayi = i - } - switch r { - case ' ', '-': - if p.moi == 0 { - p.moi = i + 1 - p.daylen = i - p.dayi - p.setDay() - } else if p.yeari == 0 { - p.yeari = i + 1 - p.molen = i - p.moi - p.set(p.moi, "Jan") - } else { - p.stateTime = timeStart - break iterRunes - } - } - case dateWeekdayAbbrevComma: - // Mon, 02 Jan 2006 15:04:05 MST - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // Thu, 4 Jan 2018 17:53:36 +0000 - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - // Mon, 02-Jan-06 15:04:05 MST - switch r { - case ' ', '-': - if p.dayi == 0 { - p.dayi = i + 1 - } else if p.moi == 0 { - p.daylen = i - p.dayi - p.setDay() - p.moi = i + 1 - } else if p.yeari == 0 { - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.yeari = i + 1 - } else { - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - break iterRunes - } - } - - default: - break iterRunes - } - } - p.coalesceDate(i) - if p.stateTime == timeStart { - // increment first one, since the i++ occurs at end of loop - if i < len(p.datestr) { - i++ - } - // ensure we skip any whitespace prefix - for ; i < len(datestr); i++ { - r := rune(datestr[i]) - if r != ' ' { - break - } - } - - iterTimeRunes: - for ; i < len(datestr); i++ { - r := rune(datestr[i]) - - // gou.Debugf("i=%d r=%s state=%d iterTimeRunes %s %s", i, string(r), p.stateTime, p.ds(), p.ts()) - - switch p.stateTime { - case timeStart: - // 22:43:22 - // 22:43 - // timeComma - // 08:20:13,787 - // timeWs - // 05:24:37 PM - // 06:20:00 UTC - // 06:20:00 UTC-05 - // 00:12:00 +0000 UTC - // 22:18:00 +0000 UTC m=+0.000000001 - // 15:04:05 -0700 - // 15:04:05 -07:00 - // 15:04:05 2008 - // timeOffset - // 03:21:51+00:00 - // 19:55:00+0100 - // timePeriod - // 17:24:37.3186369 - // 00:07:31.945167 - // 18:31:59.257000000 - // 00:00:00.000 - // timePeriodOffset - // 19:55:00.799+0100 - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 22:18:00.001 +0000 UTC m=+0.000000001 - // 00:00:00.000 +0000 UTC - // timePeriodWsAlpha - // 06:20:00.000 UTC - if p.houri == 0 { - p.houri = i - } - switch r { - case ',': - // hm, lets just swap out comma for period. for some reason go - // won't parse it. - // 2014-05-11 08:20:13,787 - ds := []byte(p.datestr) - ds[i] = '.' - return parseTime(string(ds), loc, opts...) - case '-', '+': - // 03:21:51+00:00 - p.stateTime = timeOffset - if p.seci == 0 { - // 22:18+0530 - p.minlen = i - p.mini - } else { - if p.seclen == 0 { - p.seclen = i - p.seci - } - if p.msi > 0 && p.mslen == 0 { - p.mslen = i - p.msi - } - } - p.offseti = i - case '.': - p.stateTime = timePeriod - p.seclen = i - p.seci - p.msi = i + 1 - case 'Z': - p.stateTime = timeZ - if p.seci == 0 { - p.minlen = i - p.mini - } else { - p.seclen = i - p.seci - } - // (Z)ulu time - p.loc = time.UTC - case 'a', 'A': - if p.nextIs(i, 't') || p.nextIs(i, 'T') { - // x - // September 17, 2012 at 5:00pm UTC-05 - i++ // skip t - if p.nextIs(i, ' ') { - // x - // September 17, 2012 at 5:00pm UTC-05 - i++ // skip ' - p.houri = 0 // reset hour - } - } else { - switch { - case r == 'a' && p.nextIs(i, 'm'): - p.coalesceTime(i) - p.set(i, "am") - case r == 'A' && p.nextIs(i, 'M'): - p.coalesceTime(i) - p.set(i, "PM") - } - } - - case 'p', 'P': - // Could be AM/PM - switch { - case r == 'p' && p.nextIs(i, 'm'): - p.coalesceTime(i) - p.set(i, "pm") - case r == 'P' && p.nextIs(i, 'M'): - p.coalesceTime(i) - p.set(i, "PM") - } - case ' ': - p.coalesceTime(i) - p.stateTime = timeWs - case ':': - if p.mini == 0 { - p.mini = i + 1 - p.hourlen = i - p.houri - } else if p.seci == 0 { - p.seci = i + 1 - p.minlen = i - p.mini - } else if p.seci > 0 { - // 18:31:59:257 ms uses colon, wtf - p.seclen = i - p.seci - p.set(p.seci, "05") - p.msi = i + 1 - - // gross, gross, gross. manipulating the datestr is horrible. - // https://github.com/araddon/dateparse/issues/117 - // Could not get the parsing to work using golang time.Parse() without - // replacing that colon with period. - p.set(i, ".") - datestr = datestr[0:i] + "." + datestr[i+1:] - p.datestr = datestr - } - } - case timeOffset: - // 19:55:00+0100 - // timeOffsetColon - // 15:04:05+07:00 - // 15:04:05-07:00 - if r == ':' { - p.stateTime = timeOffsetColon - } - case timeWs: - // timeWsAlpha - // 06:20:00 UTC - // 06:20:00 UTC-05 - // 15:44:11 UTC+0100 2015 - // 18:04:07 GMT+0100 (GMT Daylight Time) - // 17:57:51 MST 2009 - // timeWsAMPMMaybe - // 05:24:37 PM - // timeWsOffset - // 15:04:05 -0700 - // 00:12:00 +0000 UTC - // timeWsOffsetColon - // 15:04:05 -07:00 - // 17:57:51 -0700 2009 - // timeWsOffsetColonAlpha - // 00:12:00 +00:00 UTC - // timeWsYear - // 00:12:00 2008 - // timeZ - // 15:04:05.99Z - switch r { - case 'A', 'P': - // Could be AM/PM or could be PST or similar - p.tzi = i - p.stateTime = timeWsAMPMMaybe - case '+', '-': - p.offseti = i - p.stateTime = timeWsOffset - default: - if unicode.IsLetter(r) { - // 06:20:00 UTC - // 06:20:00 UTC-05 - // 15:44:11 UTC+0100 2015 - // 17:57:51 MST 2009 - p.tzi = i - p.stateTime = timeWsAlpha - } else if unicode.IsDigit(r) { - // 00:12:00 2008 - p.stateTime = timeWsYear - p.yeari = i - } - } - case timeWsAlpha: - // 06:20:00 UTC - // 06:20:00 UTC-05 - // timeWsAlphaWs - // 17:57:51 MST 2009 - // timeWsAlphaZoneOffset - // timeWsAlphaZoneOffsetWs - // timeWsAlphaZoneOffsetWsExtra - // 18:04:07 GMT+0100 (GMT Daylight Time) - // timeWsAlphaZoneOffsetWsYear - // 15:44:11 UTC+0100 2015 - switch r { - case '+', '-': - p.tzlen = i - p.tzi - if p.tzlen == 4 { - p.set(p.tzi, " MST") - } else if p.tzlen == 3 { - p.set(p.tzi, "MST") - } - p.stateTime = timeWsAlphaZoneOffset - p.offseti = i - case ' ': - // 17:57:51 MST 2009 - // 17:57:51 MST - p.tzlen = i - p.tzi - if p.tzlen == 4 { - p.set(p.tzi, " MST") - } else if p.tzlen == 3 { - p.set(p.tzi, "MST") - } - p.stateTime = timeWsAlphaWs - p.yeari = i + 1 - } - case timeWsAlphaWs: - // 17:57:51 MST 2009 - - case timeWsAlphaZoneOffset: - // 06:20:00 UTC-05 - // timeWsAlphaZoneOffset - // timeWsAlphaZoneOffsetWs - // timeWsAlphaZoneOffsetWsExtra - // 18:04:07 GMT+0100 (GMT Daylight Time) - // timeWsAlphaZoneOffsetWsYear - // 15:44:11 UTC+0100 2015 - switch r { - case ' ': - p.set(p.offseti, "-0700") - if p.yeari == 0 { - p.yeari = i + 1 - } - p.stateTime = timeWsAlphaZoneOffsetWs - } - case timeWsAlphaZoneOffsetWs: - // timeWsAlphaZoneOffsetWs - // timeWsAlphaZoneOffsetWsExtra - // 18:04:07 GMT+0100 (GMT Daylight Time) - // timeWsAlphaZoneOffsetWsYear - // 15:44:11 UTC+0100 2015 - if unicode.IsDigit(r) { - p.stateTime = timeWsAlphaZoneOffsetWsYear - } else { - p.extra = i - 1 - p.stateTime = timeWsAlphaZoneOffsetWsExtra - } - case timeWsAlphaZoneOffsetWsYear: - // 15:44:11 UTC+0100 2015 - if unicode.IsDigit(r) { - p.yearlen = i - p.yeari + 1 - if p.yearlen == 4 { - p.setYear() - } - } - case timeWsAMPMMaybe: - // timeWsAMPMMaybe - // timeWsAMPM - // 05:24:37 PM - // timeWsAlpha - // 00:12:00 PST - // 15:44:11 UTC+0100 2015 - if r == 'M' { - //return parse("2006-01-02 03:04:05 PM", datestr, loc) - p.stateTime = timeWsAMPM - p.set(i-1, "PM") - if p.hourlen == 2 { - p.set(p.houri, "03") - } else if p.hourlen == 1 { - p.set(p.houri, "3") - } - } else { - p.stateTime = timeWsAlpha - } - - case timeWsOffset: - // timeWsOffset - // 15:04:05 -0700 - // timeWsOffsetWsOffset - // 17:57:51 -0700 -07 - // timeWsOffsetWs - // 17:57:51 -0700 2009 - // 00:12:00 +0000 UTC - // timeWsOffsetColon - // 15:04:05 -07:00 - // timeWsOffsetColonAlpha - // 00:12:00 +00:00 UTC - switch r { - case ':': - p.stateTime = timeWsOffsetColon - case ' ': - p.set(p.offseti, "-0700") - p.yeari = i + 1 - p.stateTime = timeWsOffsetWs - } - case timeWsOffsetWs: - // 17:57:51 -0700 2009 - // 00:12:00 +0000 UTC - // 22:18:00.001 +0000 UTC m=+0.000000001 - // w Extra - // 17:57:51 -0700 -07 - switch r { - case '=': - // eff you golang - if datestr[i-1] == 'm' { - p.extra = i - 2 - p.trimExtra() - break - } - case '+', '-', '(': - // This really doesn't seem valid, but for some reason when round-tripping a go date - // their is an extra +03 printed out. seems like go bug to me, but, parsing anyway. - // 00:00:00 +0300 +03 - // 00:00:00 +0300 +0300 - p.extra = i - 1 - p.stateTime = timeWsOffset - p.trimExtra() - break - default: - switch { - case unicode.IsDigit(r): - p.yearlen = i - p.yeari + 1 - if p.yearlen == 4 { - p.setYear() - } - case unicode.IsLetter(r): - // 15:04:05 -0700 MST - if p.tzi == 0 { - p.tzi = i - } - } - } - - case timeWsOffsetColon: - // timeWsOffsetColon - // 15:04:05 -07:00 - // timeWsOffsetColonAlpha - // 2015-02-18 00:12:00 +00:00 UTC - if unicode.IsLetter(r) { - // 2015-02-18 00:12:00 +00:00 UTC - p.stateTime = timeWsOffsetColonAlpha - break iterTimeRunes - } - case timePeriod: - // 15:04:05.999999999+07:00 - // 15:04:05.999999999-07:00 - // 15:04:05.999999+07:00 - // 15:04:05.999999-07:00 - // 15:04:05.999+07:00 - // 15:04:05.999-07:00 - // timePeriod - // 17:24:37.3186369 - // 00:07:31.945167 - // 18:31:59.257000000 - // 00:00:00.000 - // timePeriodOffset - // 19:55:00.799+0100 - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // With Extra - // 00:00:00.000 +0300 +03 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // 22:18:00.001 +0000 UTC m=+0.000000001 - // timePeriodWsAlpha - // 06:20:00.000 UTC - switch r { - case ' ': - p.mslen = i - p.msi - p.stateTime = timePeriodWs - case '+', '-': - // This really shouldn't happen - p.mslen = i - p.msi - p.offseti = i - p.stateTime = timePeriodOffset - default: - if unicode.IsLetter(r) { - // 06:20:00.000 UTC - p.mslen = i - p.msi - p.stateTime = timePeriodWsAlpha - } - } - case timePeriodOffset: - // timePeriodOffset - // 19:55:00.799+0100 - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // 13:31:51.999-07:00 MST - if r == ':' { - p.stateTime = timePeriodOffsetColon - } - case timePeriodOffsetColon: - // timePeriodOffset - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // 13:31:51.999 -07:00 MST - switch r { - case ' ': - p.set(p.offseti, "-07:00") - p.stateTime = timePeriodOffsetColonWs - p.tzi = i + 1 - } - case timePeriodOffsetColonWs: - // continue - case timePeriodWs: - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // timePeriodWsOffsetColon - // 13:31:51.999 -07:00 MST - // timePeriodWsAlpha - // 06:20:00.000 UTC - if p.offseti == 0 { - p.offseti = i - } - switch r { - case '+', '-': - p.mslen = i - p.msi - 1 - p.stateTime = timePeriodWsOffset - default: - if unicode.IsLetter(r) { - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - p.stateTime = timePeriodWsOffsetWsAlpha - break iterTimeRunes - } - } - - case timePeriodWsOffset: - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // With Extra - // 00:00:00.000 +0300 +03 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // 03:02:00.001 +0300 MSK m=+0.000000001 - // timePeriodWsOffsetColon - // 13:31:51.999 -07:00 MST - // timePeriodWsAlpha - // 06:20:00.000 UTC - switch r { - case ':': - p.stateTime = timePeriodWsOffsetColon - case ' ': - p.set(p.offseti, "-0700") - case '+', '-': - // This really doesn't seem valid, but for some reason when round-tripping a go date - // their is an extra +03 printed out. seems like go bug to me, but, parsing anyway. - // 00:00:00.000 +0300 +03 - // 00:00:00.000 +0300 +0300 - p.extra = i - 1 - p.trimExtra() - break - default: - if unicode.IsLetter(r) { - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // 03:02:00.001 +0300 MSK m=+0.000000001 - p.stateTime = timePeriodWsOffsetWsAlpha - } - } - case timePeriodWsOffsetWsAlpha: - // 03:02:00.001 +0300 MSK m=+0.000000001 - // eff you golang - if r == '=' && datestr[i-1] == 'm' { - p.extra = i - 2 - p.trimExtra() - break - } - - case timePeriodWsOffsetColon: - // 13:31:51.999 -07:00 MST - switch r { - case ' ': - p.set(p.offseti, "-07:00") - default: - if unicode.IsLetter(r) { - // 13:31:51.999 -07:00 MST - p.tzi = i - p.stateTime = timePeriodWsOffsetColonAlpha - } - } - case timePeriodWsOffsetColonAlpha: - // continue - case timeZ: - // timeZ - // 15:04:05.99Z - // With a time-zone at end after Z - // 2006-01-02T15:04:05.999999999Z07:00 - // 2006-01-02T15:04:05Z07:00 - // RFC3339 = "2006-01-02T15:04:05Z07:00" - // RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" - if unicode.IsDigit(r) { - p.stateTime = timeZDigit - } - - } - } - - switch p.stateTime { - case timeWsAlpha: - switch len(p.datestr) - p.tzi { - case 3: - // 13:31:51.999 +01:00 CET - p.set(p.tzi, "MST") - case 4: - p.set(p.tzi, "MST") - p.extra = len(p.datestr) - 1 - p.trimExtra() - } - - case timeWsAlphaWs: - p.yearlen = i - p.yeari - p.setYear() - case timeWsYear: - p.yearlen = i - p.yeari - p.setYear() - case timeWsAlphaZoneOffsetWsExtra: - p.trimExtra() - case timeWsAlphaZoneOffset: - // 06:20:00 UTC-05 - if i-p.offseti < 4 { - p.set(p.offseti, "-07") - } else { - p.set(p.offseti, "-0700") - } - - case timePeriod: - p.mslen = i - p.msi - case timeOffset: - - switch len(p.datestr) - p.offseti { - case 0, 1, 2, 4: - return p, fmt.Errorf("TZ offset not recognized %q near %q (must be 2 or 4 digits optional colon)", datestr, string(datestr[p.offseti:])) - case 3: - // 19:55:00+01 - p.set(p.offseti, "-07") - case 5: - // 19:55:00+0100 - p.set(p.offseti, "-0700") - } - - case timeWsOffset: - p.set(p.offseti, "-0700") - case timeWsOffsetWs: - // 17:57:51 -0700 2009 - // 00:12:00 +0000 UTC - if p.tzi > 0 { - switch len(p.datestr) - p.tzi { - case 3: - // 13:31:51.999 +01:00 CET - p.set(p.tzi, "MST") - case 4: - // 13:31:51.999 +01:00 CEST - p.set(p.tzi, "MST ") - } - - } - case timeWsOffsetColon: - // 17:57:51 -07:00 - p.set(p.offseti, "-07:00") - case timeOffsetColon: - // 15:04:05+07:00 - p.set(p.offseti, "-07:00") - case timePeriodOffset: - // 19:55:00.799+0100 - p.set(p.offseti, "-0700") - case timePeriodOffsetColon: - p.set(p.offseti, "-07:00") - case timePeriodWsOffsetColonAlpha: - p.tzlen = i - p.tzi - switch p.tzlen { - case 3: - p.set(p.tzi, "MST") - case 4: - p.set(p.tzi, "MST ") - } - case timePeriodWsOffset: - p.set(p.offseti, "-0700") - } - p.coalesceTime(i) - } - - switch p.stateDate { - case dateDigit: - // unixy timestamps ish - // example ct type - // 1499979655583057426 19 nanoseconds - // 1499979795437000 16 micro-seconds - // 20180722105203 14 yyyyMMddhhmmss - // 1499979795437 13 milliseconds - // 1332151919 10 seconds - // 20140601 8 yyyymmdd - // 2014 4 yyyy - t := time.Time{} - if len(datestr) == len("1499979655583057426") { // 19 - // nano-seconds - if nanoSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(0, nanoSecs) - } - } else if len(datestr) == len("1499979795437000") { // 16 - // micro-seconds - if microSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(0, microSecs*1000) - } - } else if len(datestr) == len("yyyyMMddhhmmss") { // 14 - // yyyyMMddhhmmss - p.format = []byte("20060102150405") - return p, nil - } else if len(datestr) == len("1332151919000") { // 13 - if miliSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(0, miliSecs*1000*1000) - } - } else if len(datestr) == len("1332151919") { //10 - if secs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(secs, 0) - } - } else if len(datestr) == len("20140601") { - p.format = []byte("20060102") - return p, nil - } else if len(datestr) == len("2014") { - p.format = []byte("2006") - return p, nil - } else if len(datestr) < 4 { - return nil, fmt.Errorf("unrecognized format, too short %v", datestr) - } - if !t.IsZero() { - if loc == nil { - p.t = &t - return p, nil - } - t = t.In(loc) - p.t = &t - return p, nil - } - case dateDigitSt: - // 171113 14:14:20 - return p, nil - - case dateYearDash: - // 2006-01 - return p, nil - - case dateYearDashDash: - // 2006-01-02 - // 2006-1-02 - // 2006-1-2 - // 2006-01-2 - return p, nil - - case dateYearDashDashOffset: - /// 2020-07-20+00:00 - switch len(p.datestr) - p.offseti { - case 5: - p.set(p.offseti, "-0700") - case 6: - p.set(p.offseti, "-07:00") - } - return p, nil - - case dateYearDashAlphaDash: - // 2013-Feb-03 - // 2013-Feb-3 - p.daylen = i - p.dayi - p.setDay() - return p, nil - - case dateYearDashDashWs: - // 2013-04-01 - return p, nil - - case dateYearDashDashT: - return p, nil - - case dateDigitDashAlphaDash: - // 13-Feb-03 ambiguous - // 28-Feb-03 ambiguous - // 29-Jun-2016 - length := len(datestr) - (p.moi + p.molen + 1) - if length == 4 { - p.yearlen = 4 - p.set(p.yeari, "2006") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } else if length == 2 { - // We have no idea if this is - // yy-mon-dd OR dd-mon-yy - // - // We are going to ASSUME (bad, bad) that it is dd-mon-yy which is a horible assumption - p.ambiguousMD = true - p.yearlen = 2 - p.set(p.yeari, "06") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } - - return p, nil - - case dateDigitDot: - // 2014.05 - p.molen = i - p.moi - p.setMonth() - return p, nil - - case dateDigitDotDot: - // 03.31.1981 - // 3.31.2014 - // 3.2.1981 - // 3.2.81 - // 08.21.71 - // 2018.09.30 - return p, nil - - case dateDigitWsMoYear: - // 2 Jan 2018 - // 2 Jan 18 - // 2 Jan 2018 23:59 - // 02 Jan 2018 23:59 - // 12 Feb 2006, 19:17 - return p, nil - - case dateDigitWsMolong: - // 18 January 2018 - // 8 January 2018 - if p.daylen == 2 { - p.format = []byte("02 January 2006") - return p, nil - } - p.format = []byte("2 January 2006") - return p, nil // parse("2 January 2006", datestr, loc) - - case dateAlphaWsMonth: - p.yearlen = i - p.yeari - p.setYear() - return p, nil - - case dateAlphaWsMonthMore: - return p, nil - - case dateAlphaWsDigitMoreWs: - // oct 1, 1970 - p.yearlen = i - p.yeari - p.setYear() - return p, nil - - case dateAlphaWsDigitMoreWsYear: - // May 8, 2009 5:57:51 PM - // Jun 7, 2005, 05:57:51 - return p, nil - - case dateAlphaWsAlpha: - return p, nil - - case dateAlphaWsDigit: - return p, nil - - case dateAlphaWsDigitYearmaybe: - return p, nil - - case dateDigitSlash: - // 3/1/2014 - // 10/13/2014 - // 01/02/2006 - return p, nil - - case dateDigitSlashAlpha: - // 03/Jun/2014 - return p, nil - - case dateDigitYearSlash: - // 2014/10/13 - return p, nil - - case dateDigitColon: - // 3:1:2014 - // 10:13:2014 - // 01:02:2006 - // 2014:10:13 - return p, nil - - case dateDigitChineseYear: - // dateDigitChineseYear - // 2014ๅนด04ๆœˆ08ๆ—ฅ - p.format = []byte("2006ๅนด01ๆœˆ02ๆ—ฅ") - return p, nil - - case dateDigitChineseYearWs: - p.format = []byte("2006ๅนด01ๆœˆ02ๆ—ฅ 15:04:05") - return p, nil - - case dateWeekdayComma: - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // Monday, 02-Jan-06 15:04:05 MST - return p, nil - - case dateWeekdayAbbrevComma: - // Mon, 02-Jan-06 15:04:05 MST - // Mon, 02 Jan 2006 15:04:05 MST - return p, nil - - } - - return nil, unknownErr(datestr) -} - -type parser struct { - loc *time.Location - preferMonthFirst bool - retryAmbiguousDateWithSwap bool - ambiguousMD bool - stateDate dateState - stateTime timeState - format []byte - datestr string - fullMonth string - skip int - extra int - part1Len int - yeari int - yearlen int - moi int - molen int - dayi int - daylen int - houri int - hourlen int - mini int - minlen int - seci int - seclen int - msi int - mslen int - offseti int - offsetlen int - tzi int - tzlen int - t *time.Time -} - -// ParserOption defines a function signature implemented by options -// Options defined like this accept the parser and operate on the data within -type ParserOption func(*parser) error - -// PreferMonthFirst is an option that allows preferMonthFirst to be changed from its default -func PreferMonthFirst(preferMonthFirst bool) ParserOption { - return func(p *parser) error { - p.preferMonthFirst = preferMonthFirst - return nil - } -} - -// RetryAmbiguousDateWithSwap is an option that allows retryAmbiguousDateWithSwap to be changed from its default -func RetryAmbiguousDateWithSwap(retryAmbiguousDateWithSwap bool) ParserOption { - return func(p *parser) error { - p.retryAmbiguousDateWithSwap = retryAmbiguousDateWithSwap - return nil - } -} - -func newParser(dateStr string, loc *time.Location, opts ...ParserOption) *parser { - p := &parser{ - stateDate: dateStart, - stateTime: timeIgnore, - datestr: dateStr, - loc: loc, - preferMonthFirst: true, - retryAmbiguousDateWithSwap: false, - } - p.format = []byte(dateStr) - - // allow the options to mutate the parser fields from their defaults - for _, option := range opts { - option(p) - } - return p -} - -func (p *parser) nextIs(i int, b byte) bool { - if len(p.datestr) > i+1 && p.datestr[i+1] == b { - return true - } - return false -} - -func (p *parser) set(start int, val string) { - if start < 0 { - return - } - if len(p.format) < start+len(val) { - return - } - for i, r := range val { - p.format[start+i] = byte(r) - } -} -func (p *parser) setMonth() { - if p.molen == 2 { - p.set(p.moi, "01") - } else if p.molen == 1 { - p.set(p.moi, "1") - } -} - -func (p *parser) setDay() { - if p.daylen == 2 { - p.set(p.dayi, "02") - } else if p.daylen == 1 { - p.set(p.dayi, "2") - } -} -func (p *parser) setYear() { - if p.yearlen == 2 { - p.set(p.yeari, "06") - } else if p.yearlen == 4 { - p.set(p.yeari, "2006") - } -} -func (p *parser) coalesceDate(end int) { - if p.yeari > 0 { - if p.yearlen == 0 { - p.yearlen = end - p.yeari - } - p.setYear() - } - if p.moi > 0 && p.molen == 0 { - p.molen = end - p.moi - p.setMonth() - } - if p.dayi > 0 && p.daylen == 0 { - p.daylen = end - p.dayi - p.setDay() - } -} -func (p *parser) ts() string { - return fmt.Sprintf("h:(%d:%d) m:(%d:%d) s:(%d:%d)", p.houri, p.hourlen, p.mini, p.minlen, p.seci, p.seclen) -} -func (p *parser) ds() string { - return fmt.Sprintf("%s d:(%d:%d) m:(%d:%d) y:(%d:%d)", p.datestr, p.dayi, p.daylen, p.moi, p.molen, p.yeari, p.yearlen) -} -func (p *parser) coalesceTime(end int) { - // 03:04:05 - // 15:04:05 - // 3:04:05 - // 3:4:5 - // 15:04:05.00 - if p.houri > 0 { - if p.hourlen == 2 { - p.set(p.houri, "15") - } else if p.hourlen == 1 { - p.set(p.houri, "3") - } - } - if p.mini > 0 { - if p.minlen == 0 { - p.minlen = end - p.mini - } - if p.minlen == 2 { - p.set(p.mini, "04") - } else { - p.set(p.mini, "4") - } - } - if p.seci > 0 { - if p.seclen == 0 { - p.seclen = end - p.seci - } - if p.seclen == 2 { - p.set(p.seci, "05") - } else { - p.set(p.seci, "5") - } - } - - if p.msi > 0 { - for i := 0; i < p.mslen; i++ { - p.format[p.msi+i] = '0' - } - } -} -func (p *parser) setFullMonth(month string) { - if p.moi == 0 { - p.format = []byte(fmt.Sprintf("%s%s", "January", p.format[len(month):])) - } -} - -func (p *parser) trimExtra() { - if p.extra > 0 && len(p.format) > p.extra { - p.format = p.format[0:p.extra] - p.datestr = p.datestr[0:p.extra] - } -} - -// func (p *parser) remove(i, length int) { -// if len(p.format) > i+length { -// //append(a[:i], a[j:]...) -// p.format = append(p.format[0:i], p.format[i+length:]...) -// } -// if len(p.datestr) > i+length { -// //append(a[:i], a[j:]...) -// p.datestr = fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+length:]) -// } -// } - -func (p *parser) parse() (time.Time, error) { - if p.t != nil { - return *p.t, nil - } - if len(p.fullMonth) > 0 { - p.setFullMonth(p.fullMonth) - } - if p.skip > 0 && len(p.format) > p.skip { - p.format = p.format[p.skip:] - p.datestr = p.datestr[p.skip:] - } - - if p.loc == nil { - // gou.Debugf("parse layout=%q input=%q \ntx, err := time.Parse(%q, %q)", string(p.format), p.datestr, string(p.format), p.datestr) - return time.Parse(string(p.format), p.datestr) - } - //gou.Debugf("parse layout=%q input=%q \ntx, err := time.ParseInLocation(%q, %q, %v)", string(p.format), p.datestr, string(p.format), p.datestr, p.loc) - return time.ParseInLocation(string(p.format), p.datestr, p.loc) -} -func isDay(alpha string) bool { - for _, day := range days { - if alpha == day { - return true - } - } - return false -} -func isMonthFull(alpha string) bool { - for _, month := range months { - if alpha == month { - return true - } - } - return false -} diff --git a/vendor/github.com/jinzhu/now/Guardfile b/vendor/github.com/jinzhu/now/Guardfile new file mode 100644 index 00000000000..0b860b0653c --- /dev/null +++ b/vendor/github.com/jinzhu/now/Guardfile @@ -0,0 +1,3 @@ +guard 'gotest' do + watch(%r{\.go$}) +end diff --git a/vendor/github.com/araddon/dateparse/LICENSE b/vendor/github.com/jinzhu/now/License similarity index 95% rename from vendor/github.com/araddon/dateparse/LICENSE rename to vendor/github.com/jinzhu/now/License index f675ed313ac..037e1653e69 100644 --- a/vendor/github.com/araddon/dateparse/LICENSE +++ b/vendor/github.com/jinzhu/now/License @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2017 Aaron Raddon +Copyright (c) 2013-NOW Jinzhu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/jinzhu/now/README.md b/vendor/github.com/jinzhu/now/README.md new file mode 100644 index 00000000000..e81d31de6a7 --- /dev/null +++ b/vendor/github.com/jinzhu/now/README.md @@ -0,0 +1,137 @@ +## Now + +Now is a time toolkit for golang + +[![go report card](https://goreportcard.com/badge/github.com/jinzhu/now "go report card")](https://goreportcard.com/report/github.com/jinzhu/now) +[![test status](https://github.com/jinzhu/now/workflows/tests/badge.svg?branch=master "test status")](https://github.com/jinzhu/now/actions) +[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) + +## Install + +``` +go get -u github.com/jinzhu/now +``` + +## Usage + +Calculating time based on current time + +```go +import "github.com/jinzhu/now" + +time.Now() // 2013-11-18 17:51:49.123456789 Mon + +now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon +now.BeginningOfHour() // 2013-11-18 17:00:00 Mon +now.BeginningOfDay() // 2013-11-18 00:00:00 Mon +now.BeginningOfWeek() // 2013-11-17 00:00:00 Sun +now.BeginningOfMonth() // 2013-11-01 00:00:00 Fri +now.BeginningOfQuarter() // 2013-10-01 00:00:00 Tue +now.BeginningOfYear() // 2013-01-01 00:00:00 Tue + +now.EndOfMinute() // 2013-11-18 17:51:59.999999999 Mon +now.EndOfHour() // 2013-11-18 17:59:59.999999999 Mon +now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon +now.EndOfWeek() // 2013-11-23 23:59:59.999999999 Sat +now.EndOfMonth() // 2013-11-30 23:59:59.999999999 Sat +now.EndOfQuarter() // 2013-12-31 23:59:59.999999999 Tue +now.EndOfYear() // 2013-12-31 23:59:59.999999999 Tue + +now.WeekStartDay = time.Monday // Set Monday as first day, default is Sunday +now.EndOfWeek() // 2013-11-24 23:59:59.999999999 Sun +``` + +Calculating time based on another time + +```go +t := time.Date(2013, 02, 18, 17, 51, 49, 123456789, time.Now().Location()) +now.With(t).EndOfMonth() // 2013-02-28 23:59:59.999999999 Thu +``` + +Calculating time based on configuration + +```go +location, err := time.LoadLocation("Asia/Shanghai") + +myConfig := &now.Config{ + WeekStartDay: time.Monday, + TimeLocation: location, + TimeFormats: []string{"2006-01-02 15:04:05"}, +} + +t := time.Date(2013, 11, 18, 17, 51, 49, 123456789, time.Now().Location()) // // 2013-11-18 17:51:49.123456789 Mon +myConfig.With(t).BeginningOfWeek() // 2013-11-18 00:00:00 Mon + +myConfig.Parse("2002-10-12 22:14:01") // 2002-10-12 22:14:01 +myConfig.Parse("2002-10-12 22:14") // returns error 'can't parse string as time: 2002-10-12 22:14' +``` + +### Monday/Sunday + +Don't be bothered with the `WeekStartDay` setting, you can use `Monday`, `Sunday` + +```go +now.Monday() // 2013-11-18 00:00:00 Mon +now.Monday("17:44") // 2013-11-18 17:44:00 Mon +now.Sunday() // 2013-11-24 00:00:00 Sun (Next Sunday) +now.Sunday("18:19:24") // 2013-11-24 18:19:24 Sun (Next Sunday) +now.EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of next Sunday) + +t := time.Date(2013, 11, 24, 17, 51, 49, 123456789, time.Now().Location()) // 2013-11-24 17:51:49.123456789 Sun +now.With(t).Monday() // 2013-11-18 00:00:00 Mon (Last Monday if today is Sunday) +now.With(t).Monday("17:44") // 2013-11-18 17:44:00 Mon (Last Monday if today is Sunday) +now.With(t).Sunday() // 2013-11-24 00:00:00 Sun (Beginning Of Today if today is Sunday) +now.With(t).Sunday("18:19:24") // 2013-11-24 18:19:24 Sun (Beginning Of Today if today is Sunday) +now.With(t).EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of Today if today is Sunday) +``` + +### Parse String to Time + +```go +time.Now() // 2013-11-18 17:51:49.123456789 Mon + +// Parse(string) (time.Time, error) +t, err := now.Parse("2017") // 2017-01-01 00:00:00, nil +t, err := now.Parse("2017-10") // 2017-10-01 00:00:00, nil +t, err := now.Parse("2017-10-13") // 2017-10-13 00:00:00, nil +t, err := now.Parse("1999-12-12 12") // 1999-12-12 12:00:00, nil +t, err := now.Parse("1999-12-12 12:20") // 1999-12-12 12:20:00, nil +t, err := now.Parse("1999-12-12 12:20:21") // 1999-12-12 12:20:21, nil +t, err := now.Parse("10-13") // 2013-10-13 00:00:00, nil +t, err := now.Parse("12:20") // 2013-11-18 12:20:00, nil +t, err := now.Parse("12:20:13") // 2013-11-18 12:20:13, nil +t, err := now.Parse("14") // 2013-11-18 14:00:00, nil +t, err := now.Parse("99:99") // 2013-11-18 12:20:00, Can't parse string as time: 99:99 + +// MustParse must parse string to time or it will panic +now.MustParse("2013-01-13") // 2013-01-13 00:00:00 +now.MustParse("02-17") // 2013-02-17 00:00:00 +now.MustParse("2-17") // 2013-02-17 00:00:00 +now.MustParse("8") // 2013-11-18 08:00:00 +now.MustParse("2002-10-12 22:14") // 2002-10-12 22:14:00 +now.MustParse("99:99") // panic: Can't parse string as time: 99:99 +``` + +Extend `now` to support more formats is quite easy, just update `now.TimeFormats` with other time layouts, e.g: + +```go +now.TimeFormats = append(now.TimeFormats, "02 Jan 2006 15:04") +``` + +Please send me pull requests if you want a format to be supported officially + +## Contributing + +You can help to make the project better, check out [http://gorm.io/contribute.html](http://gorm.io/contribute.html) for things you can do. + +# Author + +**jinzhu** + +* +* +* + +## License + +Released under the [MIT License](http://www.opensource.org/licenses/MIT). diff --git a/vendor/github.com/jinzhu/now/main.go b/vendor/github.com/jinzhu/now/main.go new file mode 100644 index 00000000000..8f78bc75219 --- /dev/null +++ b/vendor/github.com/jinzhu/now/main.go @@ -0,0 +1,200 @@ +// Package now is a time toolkit for golang. +// +// More details README here: https://github.com/jinzhu/now +// +// import "github.com/jinzhu/now" +// +// now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon +// now.BeginningOfDay() // 2013-11-18 00:00:00 Mon +// now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon +package now + +import "time" + +// WeekStartDay set week start day, default is sunday +var WeekStartDay = time.Sunday + +// TimeFormats default time formats will be parsed as +var TimeFormats = []string{ + "2006", "2006-1", "2006-1-2", "2006-1-2 15", "2006-1-2 15:4", "2006-1-2 15:4:5", "1-2", + "15:4:5", "15:4", "15", + "15:4:5 Jan 2, 2006 MST", "2006-01-02 15:04:05.999999999 -0700 MST", "2006-01-02T15:04:05Z0700", "2006-01-02T15:04:05Z07", + "2006.1.2", "2006.1.2 15:04:05", "2006.01.02", "2006.01.02 15:04:05", "2006.01.02 15:04:05.999999999", + "1/2/2006", "1/2/2006 15:4:5", "2006/01/02", "20060102", "2006/01/02 15:04:05", + time.ANSIC, time.UnixDate, time.RubyDate, time.RFC822, time.RFC822Z, time.RFC850, + time.RFC1123, time.RFC1123Z, time.RFC3339, time.RFC3339Nano, + time.Kitchen, time.Stamp, time.StampMilli, time.StampMicro, time.StampNano, +} + +// Config configuration for now package +type Config struct { + WeekStartDay time.Weekday + TimeLocation *time.Location + TimeFormats []string +} + +// DefaultConfig default config +var DefaultConfig *Config + +// New initialize Now based on configuration +func (config *Config) With(t time.Time) *Now { + return &Now{Time: t, Config: config} +} + +// Parse parse string to time based on configuration +func (config *Config) Parse(strs ...string) (time.Time, error) { + if config.TimeLocation == nil { + return config.With(time.Now()).Parse(strs...) + } else { + return config.With(time.Now().In(config.TimeLocation)).Parse(strs...) + } +} + +// MustParse must parse string to time or will panic +func (config *Config) MustParse(strs ...string) time.Time { + if config.TimeLocation == nil { + return config.With(time.Now()).MustParse(strs...) + } else { + return config.With(time.Now().In(config.TimeLocation)).MustParse(strs...) + } +} + +// Now now struct +type Now struct { + time.Time + *Config +} + +// With initialize Now with time +func With(t time.Time) *Now { + config := DefaultConfig + if config == nil { + config = &Config{ + WeekStartDay: WeekStartDay, + TimeFormats: TimeFormats, + } + } + + return &Now{Time: t, Config: config} +} + +// New initialize Now with time +func New(t time.Time) *Now { + return With(t) +} + +// BeginningOfMinute beginning of minute +func BeginningOfMinute() time.Time { + return With(time.Now()).BeginningOfMinute() +} + +// BeginningOfHour beginning of hour +func BeginningOfHour() time.Time { + return With(time.Now()).BeginningOfHour() +} + +// BeginningOfDay beginning of day +func BeginningOfDay() time.Time { + return With(time.Now()).BeginningOfDay() +} + +// BeginningOfWeek beginning of week +func BeginningOfWeek() time.Time { + return With(time.Now()).BeginningOfWeek() +} + +// BeginningOfMonth beginning of month +func BeginningOfMonth() time.Time { + return With(time.Now()).BeginningOfMonth() +} + +// BeginningOfQuarter beginning of quarter +func BeginningOfQuarter() time.Time { + return With(time.Now()).BeginningOfQuarter() +} + +// BeginningOfYear beginning of year +func BeginningOfYear() time.Time { + return With(time.Now()).BeginningOfYear() +} + +// EndOfMinute end of minute +func EndOfMinute() time.Time { + return With(time.Now()).EndOfMinute() +} + +// EndOfHour end of hour +func EndOfHour() time.Time { + return With(time.Now()).EndOfHour() +} + +// EndOfDay end of day +func EndOfDay() time.Time { + return With(time.Now()).EndOfDay() +} + +// EndOfWeek end of week +func EndOfWeek() time.Time { + return With(time.Now()).EndOfWeek() +} + +// EndOfMonth end of month +func EndOfMonth() time.Time { + return With(time.Now()).EndOfMonth() +} + +// EndOfQuarter end of quarter +func EndOfQuarter() time.Time { + return With(time.Now()).EndOfQuarter() +} + +// EndOfYear end of year +func EndOfYear() time.Time { + return With(time.Now()).EndOfYear() +} + +// Monday monday + +func Monday(strs ...string) time.Time { + return With(time.Now()).Monday(strs...) +} + +// Sunday sunday +func Sunday(strs ...string) time.Time { + return With(time.Now()).Sunday(strs...) +} + +// EndOfSunday end of sunday +func EndOfSunday() time.Time { + return With(time.Now()).EndOfSunday() +} + +// Quarter returns the yearly quarter +func Quarter() uint { + return With(time.Now()).Quarter() +} + +// Parse parse string to time +func Parse(strs ...string) (time.Time, error) { + return With(time.Now()).Parse(strs...) +} + +// ParseInLocation parse string to time in location +func ParseInLocation(loc *time.Location, strs ...string) (time.Time, error) { + return With(time.Now().In(loc)).Parse(strs...) +} + +// MustParse must parse string to time or will panic +func MustParse(strs ...string) time.Time { + return With(time.Now()).MustParse(strs...) +} + +// MustParseInLocation must parse string to time in location or will panic +func MustParseInLocation(loc *time.Location, strs ...string) time.Time { + return With(time.Now().In(loc)).MustParse(strs...) +} + +// Between check now between the begin, end time or not +func Between(time1, time2 string) bool { + return With(time.Now()).Between(time1, time2) +} diff --git a/vendor/github.com/jinzhu/now/now.go b/vendor/github.com/jinzhu/now/now.go new file mode 100644 index 00000000000..2f524cc8d4b --- /dev/null +++ b/vendor/github.com/jinzhu/now/now.go @@ -0,0 +1,245 @@ +package now + +import ( + "errors" + "regexp" + "time" +) + +// BeginningOfMinute beginning of minute +func (now *Now) BeginningOfMinute() time.Time { + return now.Truncate(time.Minute) +} + +// BeginningOfHour beginning of hour +func (now *Now) BeginningOfHour() time.Time { + y, m, d := now.Date() + return time.Date(y, m, d, now.Time.Hour(), 0, 0, 0, now.Time.Location()) +} + +// BeginningOfDay beginning of day +func (now *Now) BeginningOfDay() time.Time { + y, m, d := now.Date() + return time.Date(y, m, d, 0, 0, 0, 0, now.Time.Location()) +} + +// BeginningOfWeek beginning of week +func (now *Now) BeginningOfWeek() time.Time { + t := now.BeginningOfDay() + weekday := int(t.Weekday()) + + if now.WeekStartDay != time.Sunday { + weekStartDayInt := int(now.WeekStartDay) + + if weekday < weekStartDayInt { + weekday = weekday + 7 - weekStartDayInt + } else { + weekday = weekday - weekStartDayInt + } + } + return t.AddDate(0, 0, -weekday) +} + +// BeginningOfMonth beginning of month +func (now *Now) BeginningOfMonth() time.Time { + y, m, _ := now.Date() + return time.Date(y, m, 1, 0, 0, 0, 0, now.Location()) +} + +// BeginningOfQuarter beginning of quarter +func (now *Now) BeginningOfQuarter() time.Time { + month := now.BeginningOfMonth() + offset := (int(month.Month()) - 1) % 3 + return month.AddDate(0, -offset, 0) +} + +// BeginningOfHalf beginning of half year +func (now *Now) BeginningOfHalf() time.Time { + month := now.BeginningOfMonth() + offset := (int(month.Month()) - 1) % 6 + return month.AddDate(0, -offset, 0) +} + +// BeginningOfYear BeginningOfYear beginning of year +func (now *Now) BeginningOfYear() time.Time { + y, _, _ := now.Date() + return time.Date(y, time.January, 1, 0, 0, 0, 0, now.Location()) +} + +// EndOfMinute end of minute +func (now *Now) EndOfMinute() time.Time { + return now.BeginningOfMinute().Add(time.Minute - time.Nanosecond) +} + +// EndOfHour end of hour +func (now *Now) EndOfHour() time.Time { + return now.BeginningOfHour().Add(time.Hour - time.Nanosecond) +} + +// EndOfDay end of day +func (now *Now) EndOfDay() time.Time { + y, m, d := now.Date() + return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), now.Location()) +} + +// EndOfWeek end of week +func (now *Now) EndOfWeek() time.Time { + return now.BeginningOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond) +} + +// EndOfMonth end of month +func (now *Now) EndOfMonth() time.Time { + return now.BeginningOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond) +} + +// EndOfQuarter end of quarter +func (now *Now) EndOfQuarter() time.Time { + return now.BeginningOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond) +} + +// EndOfHalf end of half year +func (now *Now) EndOfHalf() time.Time { + return now.BeginningOfHalf().AddDate(0, 6, 0).Add(-time.Nanosecond) +} + +// EndOfYear end of year +func (now *Now) EndOfYear() time.Time { + return now.BeginningOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond) +} + +// Monday monday +/* +func (now *Now) Monday() time.Time { + t := now.BeginningOfDay() + weekday := int(t.Weekday()) + if weekday == 0 { + weekday = 7 + } + return t.AddDate(0, 0, -weekday+1) +} +*/ + +func (now *Now) Monday(strs ...string) time.Time { + var parseTime time.Time + var err error + if len(strs) > 0 { + parseTime, err = now.Parse(strs...) + if err != nil { + panic(err) + } + } else { + parseTime = now.BeginningOfDay() + } + weekday := int(parseTime.Weekday()) + if weekday == 0 { + weekday = 7 + } + return parseTime.AddDate(0, 0, -weekday+1) +} + +func (now *Now) Sunday(strs ...string) time.Time { + var parseTime time.Time + var err error + if len(strs) > 0 { + parseTime, err = now.Parse(strs...) + if err != nil { + panic(err) + } + } else { + parseTime = now.BeginningOfDay() + } + weekday := int(parseTime.Weekday()) + if weekday == 0 { + weekday = 7 + } + return parseTime.AddDate(0, 0, (7 - weekday)) +} + +// EndOfSunday end of sunday +func (now *Now) EndOfSunday() time.Time { + return New(now.Sunday()).EndOfDay() +} + +// Quarter returns the yearly quarter +func (now *Now) Quarter() uint { + return (uint(now.Month())-1)/3 + 1 +} + +func (now *Now) parseWithFormat(str string, location *time.Location) (t time.Time, err error) { + for _, format := range now.TimeFormats { + t, err = time.ParseInLocation(format, str, location) + + if err == nil { + return + } + } + err = errors.New("Can't parse string as time: " + str) + return +} + +var hasTimeRegexp = regexp.MustCompile(`(\s+|^\s*|T)\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))(\s*$|[Z+-])`) // match 15:04:05, 15:04:05.000, 15:04:05.000000 15, 2017-01-01 15:04, 2021-07-20T00:59:10Z, 2021-07-20T00:59:10+08:00, 2021-07-20T00:00:10-07:00 etc +var onlyTimeRegexp = regexp.MustCompile(`^\s*\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))\s*$`) // match 15:04:05, 15, 15:04:05.000, 15:04:05.000000, etc + +// Parse parse string to time +func (now *Now) Parse(strs ...string) (t time.Time, err error) { + var ( + setCurrentTime bool + parseTime []int + currentLocation = now.Location() + onlyTimeInStr = true + currentTime = formatTimeToList(now.Time) + ) + + for _, str := range strs { + hasTimeInStr := hasTimeRegexp.MatchString(str) // match 15:04:05, 15 + onlyTimeInStr = hasTimeInStr && onlyTimeInStr && onlyTimeRegexp.MatchString(str) + if t, err = now.parseWithFormat(str, currentLocation); err == nil { + location := t.Location() + parseTime = formatTimeToList(t) + + for i, v := range parseTime { + // Don't reset hour, minute, second if current time str including time + if hasTimeInStr && i <= 3 { + continue + } + + // If value is zero, replace it with current time + if v == 0 { + if setCurrentTime { + parseTime[i] = currentTime[i] + } + } else { + setCurrentTime = true + } + + // if current time only includes time, should change day, month to current time + if onlyTimeInStr { + if i == 4 || i == 5 { + parseTime[i] = currentTime[i] + continue + } + } + } + + t = time.Date(parseTime[6], time.Month(parseTime[5]), parseTime[4], parseTime[3], parseTime[2], parseTime[1], parseTime[0], location) + currentTime = formatTimeToList(t) + } + } + return +} + +// MustParse must parse string to time or it will panic +func (now *Now) MustParse(strs ...string) (t time.Time) { + t, err := now.Parse(strs...) + if err != nil { + panic(err) + } + return t +} + +// Between check time between the begin, end time or not +func (now *Now) Between(begin, end string) bool { + beginTime := now.MustParse(begin) + endTime := now.MustParse(end) + return now.After(beginTime) && now.Before(endTime) +} diff --git a/vendor/github.com/jinzhu/now/time.go b/vendor/github.com/jinzhu/now/time.go new file mode 100644 index 00000000000..52dd8b2a098 --- /dev/null +++ b/vendor/github.com/jinzhu/now/time.go @@ -0,0 +1,9 @@ +package now + +import "time" + +func formatTimeToList(t time.Time) []int { + hour, min, sec := t.Clock() + year, month, day := t.Date() + return []int{t.Nanosecond(), sec, min, hour, day, int(month), year} +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d92e2eb8ccf..ec74bc64f87 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -78,9 +78,6 @@ github.com/alexedwards/argon2id # github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 ## explicit github.com/amoghe/go-crypt -# github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de -## explicit; go 1.12 -github.com/araddon/dateparse # github.com/armon/go-metrics v0.4.1 ## explicit; go 1.12 github.com/armon/go-metrics @@ -1172,6 +1169,9 @@ github.com/jellydator/ttlcache/v2 # github.com/jellydator/ttlcache/v3 v3.1.0 ## explicit; go 1.18 github.com/jellydator/ttlcache/v3 +# github.com/jinzhu/now v1.1.5 +## explicit; go 1.12 +github.com/jinzhu/now # github.com/jmespath/go-jmespath v0.4.0 ## explicit; go 1.14 github.com/jmespath/go-jmespath From 9ac725ca81ff7b3b167bf82bff9ec76ddbd89dd3 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Mon, 11 Sep 2023 16:03:04 +0200 Subject: [PATCH 10/11] enhancement: structure kql parser tests into logical clusters --- .../unreleased/kql-search-query-language.md | 30 +- .../search/pkg/query/kql/dictionary_test.go | 2142 +++++++++-------- 2 files changed, 1114 insertions(+), 1058 deletions(-) diff --git a/changelog/unreleased/kql-search-query-language.md b/changelog/unreleased/kql-search-query-language.md index 0749e09dff9..f1724114ca8 100644 --- a/changelog/unreleased/kql-search-query-language.md +++ b/changelog/unreleased/kql-search-query-language.md @@ -2,16 +2,31 @@ Enhancement: Keyword Query Language (KQL) search syntax We've introduced support for [KQL](https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference) as the default oCIS search query language. -Some examples of a valid KQL query are: - -* `Tag`: `tag:golden tag:"silver"` -* `Filename`: `name:file.txt name:"file.docx"` -* `Content`: `content:ahab content:"captain aha*"` +Simple queries: + +* `tag:golden tag:"silver"` +* `name:file.txt name:"file.docx"` +* `content:ahab content:"captain aha*"` + +Date/-range queries + +* `Mtime:"2023-09-05T08:42:11.23554+02:00"` +* `Mtime>"2023-09-05T08:42:11.23554+02:00"` +* `Mtime>="2023-09-05T08:42:11.23554+02:00"` +* `Mtime<"2023-09-05T08:42:11.23554+02:00"` +* `Mtime<="2023-09-05T08:42:11.23554+02:00"` +* `Mtime:today` - range: start of today till end of today +* `Mtime:yesterday` - range: start of yesterday till end of yesterday +* `Mtime:"this week"` - range: start of this week till end of this week +* `Mtime:"this month"` - range: start of this month till end of this month +* `Mtime:"last month"` - range: start of last month till end of last month +* `Mtime:"this year"` - range: start of this year till end of this year +* `Mtime:"last year"` - range: start of last year till end of last year Conjunctive normal form queries: -* `Boolean`: `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver` -* `Group`: `(tag:book content:ahab*)`, `tag:(book pdf)` +* `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver` +* `(tag:book content:ahab*)`, `tag:(book pdf)` Complex queries: @@ -22,6 +37,7 @@ https://github.com/owncloud/ocis/pull/7043 https://github.com/owncloud/ocis/pull/7247 https://github.com/owncloud/ocis/pull/7248 https://github.com/owncloud/ocis/pull/7254 +https://github.com/owncloud/ocis/pull/7262 https://github.com/owncloud/web/pull/9653 https://github.com/owncloud/web/pull/9672 https://github.com/owncloud/ocis/issues/7042 diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index 30a91e21f33..34edf4e230e 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -1,6 +1,7 @@ package kql_test import ( + "strconv" "strings" "testing" "time" @@ -34,6 +35,18 @@ var setWorldClock = func(t *testing.T, ts string) func() func() { } } +var patchNow = func(t *testing.T, ts string) func() func() { + return func() func() { + kql.PatchTimeNow(func() time.Time { + return mustParseTime(t, ts) + }) + + return func() { + kql.PatchTimeNow(time.Now) + } + } +} + var join = func(v []string) string { return strings.Join(v, " ") } @@ -66,1176 +79,1187 @@ var FullDictionary = []string{ } func TestParse(t *testing.T) { + type tc struct { + name string + query string + ast *ast.Ast + error error + skip bool + patch func() func() + } + tests := []struct { - name string - skip bool - givenQuery string - expectedAst *ast.Ast - expectedError error - prepare func() func() + name string + cases []tc }{ - // SPEC ////////////////////////////////////////////////////////////////////////////// - // - // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf - // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 - // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference - // - // ++ - // 2.1.2 AND Operator - // 3.1.2 AND Operator - { - name: `cat AND dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - name: `AND`, - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: `AND cat AND dog`, - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - // ++ - // 2.1.6 NOT Operator - // 3.1.6 NOT Operator - { - name: `cat NOT dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - name: `NOT dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - // ++ - // 2.1.8 OR Operator - // 3.1.8 OR Operator - { - name: `cat OR dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - name: `OR`, - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: `OR cat AND dog`, - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - // ++ - // 3.1.11 Implicit Operator - { - name: `cat dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - name: `cat AND (dog OR fox)`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "fox"}, - }}, - }, - }, - }, - { - name: `cat (dog OR fox)`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "fox"}, - }}, - }, - }, - }, - // ++ - // 2.1.12 Parentheses - // 3.1.12 Parentheses - { - name: `(cat OR dog) AND fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "dog"}, - }}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, - }, - }, - }, - // ++ - // 3.2.3 Implicit Operator for Property Restriction - { - name: `author:"John Smith" filetype:docx`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - }, - }, - }, - { - name: `author:"John Smith" AND filetype:docx`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - }, - }, - }, - { - name: `author:"John Smith" author:"Jane Smith"`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Jane Smith"}, - }, - }, - }, - { - name: `author:"John Smith" OR author:"Jane Smith"`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Jane Smith"}, - }, - }, - }, - { - name: `cat filetype:docx`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - }, - }, - }, - { - name: `cat AND filetype:docx`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - }, - }, - }, - // ++ - // 3.3.1.1.1 Implicit AND Operator - { - name: `cat +dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - name: `cat AND dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, { - name: `cat -dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, + // SPEC ////////////////////////////////////////////////////////////////////////////// + // + // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf + // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 + // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference + name: `spec`, + cases: []tc{ + // 2.1.2 AND Operator + // 3.1.2 AND Operator + { + query: `cat AND dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, }, - }, - }, - { - name: `cat AND NOT dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, + { + query: `AND`, + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, }, - }, - }, - { - name: `cat +dog -fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, + { + query: `AND cat AND dog`, + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, }, - }, - }, - { - name: `cat AND dog AND NOT fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, + // 2.1.6 NOT Operator + // 3.1.6 NOT Operator + { + query: `cat NOT dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, + }, + }, }, - }, - }, - { - name: `cat dog +fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, + { + query: `NOT dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, + }, + }, }, - }, - }, - { - name: `fox OR (fox AND (cat OR dog))`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "fox"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "fox"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ + // 2.1.8 OR Operator + // 3.1.8 OR Operator + { + query: `cat OR dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolOR}, &ast.StringNode{Value: "dog"}, - }}, - }}, - }, - }, - }, - { - name: `cat dog -fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, + }, + }, }, - }, - }, - { - name: `(NOT fox) AND (cat OR dog)`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, - }}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "dog"}, - }}, + { + query: `OR`, + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, }, - }, - }, - { - name: `cat +dog -fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, + { + query: `OR cat AND dog`, + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, }, - }, - }, - { - name: `(NOT fox) AND (dog OR (dog AND cat))`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, - }}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "dog"}, + // 3.1.11 Implicit Operator + { + query: `cat dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + }, + { + query: `cat AND (dog OR fox)`, + ast: &ast.Ast{ + Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, - }}, - }}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "fox"}, + }}, + }, + }, }, - }, - }, - // ++ - // 2.3.5 Date Tokens - // 3.3.5 Date Tokens - { - name: `Modified:2023-09-05`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05"), + { + query: `cat (dog OR fox)`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "fox"}, + }}, + }, }, }, - }, - }, - { - name: `Modified:"2008-01-29"`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2008-01-29"), + // 2.1.12 Parentheses + // 3.1.12 Parentheses + { + query: `(cat OR dog) AND fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "dog"}, + }}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, + }, }, }, - }, - }, - { - name: `Modified:today`, - prepare: setWorldClock(t, "2023-09-10"), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-10"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + // 3.2.3 Implicit Operator for Property Restriction + { + query: `author:"John Smith" filetype:docx`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, + }, }, }, - }, - }, - ////////////////////////////////////////////////////////////////////////////////////// - // everything else - { - name: "FullDictionary", - givenQuery: join(FullDictionary), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "federated"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "federat*"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fed*"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filename", Value: "budget.xlsx"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "Shakespear"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Paul"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Shakesp*"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "title", Value: "Advanced Search"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "title", Value: "Advanced Sear*"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "title", Value: "Advan* Search"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "title", Value: "*anced Search"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Jane Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{ - Key: "author", + { + query: `author:"John Smith" AND filetype:docx`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, + &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "Jane Smith"}, + &ast.StringNode{Key: "filetype", Value: "docx"}, }, }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.GroupNode{ - Key: "author", + }, + { + query: `author:"John Smith" author:"Jane Smith"`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, + &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Jane Smith"}, + &ast.StringNode{Key: "author", Value: "Jane Smith"}, }, }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{ + }, + { + query: `author:"John Smith" OR author:"Jane Smith"`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Key: "DepartmentId", Value: "*"}, + &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "RelatedHubSites", Value: "*"}, + &ast.StringNode{Key: "author", Value: "Jane Smith"}, }, }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "contentclass", Value: "sts_site"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.BooleanNode{Key: "IsHubSite", Value: false}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{ + }, + { + query: `cat filetype:docx`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Key: "filetype", Value: "docx"}, + &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "title", Value: "Advanced Search"}, + &ast.StringNode{Key: "filetype", Value: "docx"}, }, }, }, - }, - }, - { - name: "Group", - givenQuery: join([]string{ - `(name:"moby di*" OR tag:bestseller) AND tag:book NOT tag:read`, - `author:("John Smith" Jane)`, - `author:("John Smith" OR Jane)`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ + { + query: `cat AND filetype:docx`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Key: "name", Value: "moby di*"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "tag", Value: "bestseller"}, + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, }, }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "tag", Value: "book"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Key: "tag", Value: "read"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{ - Key: "author", + }, + // 3.3.1.1.1 Implicit AND Operator + { + query: `cat +dog`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, + &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "Jane"}, + &ast.StringNode{Value: "dog"}, }, }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.GroupNode{ - Key: "author", + }, + { + query: `cat AND dog`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Jane"}, + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, }, }, }, - }, - }, - { - name: `author:("John Smith" Jane) author:"Jack" AND author:"Oggy"`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "author", + { + query: `cat -dog`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, + &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "Jane"}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, }, }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Jack"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "Oggy"}, }, - }, - }, - { - name: `author:("John Smith" OR Jane)`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "author", + { + query: `cat AND NOT dog`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Jane"}, + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, }, }, }, - }, - }, - { - name: `NOT "John Smith" NOT Jane`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "Jane"}, - }, - }, - }, - { - name: `NOT author:"John Smith" NOT author:"Jane Smith" NOT tag:sifi`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Key: "author", Value: "Jane Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Key: "tag", Value: "sifi"}, - }, - }, - }, - { - name: `scope:"/new folder/subfolder" file`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{ - Key: "scope", - Value: "/new folder/subfolder", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "file", + { + query: `cat +dog -fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, + }, }, }, - }, - }, - { - name: ` ๐Ÿ˜‚ "*๐Ÿ˜€ ๐Ÿ˜*" name:๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ๐ŸŽ๐Ÿ˜ name:๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ ๐Ÿ˜`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{ - Value: "๐Ÿ˜‚", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "*๐Ÿ˜€ ๐Ÿ˜*", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Key: "name", - Value: "๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ๐ŸŽ๐Ÿ˜", - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{ - Key: "name", - Value: "๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "๐Ÿ˜", + { + query: `cat AND dog AND NOT fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + query: `cat dog +fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + query: `fox OR (fox AND (cat OR dog))`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "fox"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "fox"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "dog"}, + }}, + }}, + }, + }, + }, + { + query: `cat dog -fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + query: `(NOT fox) AND (cat OR dog)`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, + }}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "dog"}, + }}, + }, + }, + }, + { + query: `cat +dog -fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + query: `(NOT fox) AND (dog OR (dog AND cat))`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, + }}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "cat"}, + }}, + }}, + }, + }, + }, + // 2.3.5 Date Tokens + // 3.3.5 Date Tokens + { + query: `Modified:2023-09-05`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05"), + }, + }, + }, + }, + { + query: `Modified:"2008-01-29"`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2008-01-29"), + }, + }, + }, + }, + { + query: `Modified:today`, + patch: patchNow(t, "2023-09-10"), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, }, }, }, }, { name: "DateTimeRestrictionNode", - givenQuery: join([]string{ - `Mtime:"2023-09-05T08:42:11.23554+02:00"`, - `Mtime:2023-09-05T08:42:11.23554+02:00`, - `Mtime="2023-09-05T08:42:11.23554+02:00"`, - `Mtime=2023-09-05T08:42:11.23554+02:00`, - `Mtime<"2023-09-05T08:42:11.23554+02:00"`, - `Mtime<2023-09-05T08:42:11.23554+02:00`, - `Mtime<="2023-09-05T08:42:11.23554+02:00"`, - `Mtime<=2023-09-05T08:42:11.23554+02:00`, - `Mtime>"2023-09-05T08:42:11.23554+02:00"`, - `Mtime>2023-09-05T08:42:11.23554+02:00`, - `Mtime>="2023-09-05T08:42:11.23554+02:00"`, - `Mtime>=2023-09-05T08:42:11.23554+02:00`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + cases: []tc{ + { + query: join([]string{ + `Mtime:"2023-09-05T08:42:11.23554+02:00"`, + `Mtime:2023-09-05T08:42:11.23554+02:00`, + `Mtime="2023-09-05T08:42:11.23554+02:00"`, + `Mtime=2023-09-05T08:42:11.23554+02:00`, + `Mtime<"2023-09-05T08:42:11.23554+02:00"`, + `Mtime<2023-09-05T08:42:11.23554+02:00`, + `Mtime<="2023-09-05T08:42:11.23554+02:00"`, + `Mtime<=2023-09-05T08:42:11.23554+02:00`, + `Mtime>"2023-09-05T08:42:11.23554+02:00"`, + `Mtime>2023-09-05T08:42:11.23554+02:00`, + `Mtime>="2023-09-05T08:42:11.23554+02:00"`, + `Mtime>=2023-09-05T08:42:11.23554+02:00`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + }, }, }, - }, - }, - { - name: "NaturalLanguage DateTimeNode - today", - prepare: setWorldClock(t, "2023-09-10"), - givenQuery: join([]string{ - `Mtime:today`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-10"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + { + name: "NaturalLanguage DateTimeNode - today", + patch: setWorldClock(t, "2023-09-10"), + query: join([]string{ + `Mtime:today`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, }, }, - }, - }, - { - name: "NaturalLanguage DateTimeNode - yesterday", - prepare: setWorldClock(t, "2023-09-10"), - givenQuery: join([]string{ - `Mtime:yesterday`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-09"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-09 23:59:59.999999999"), + { + name: "NaturalLanguage DateTimeNode - yesterday", + patch: setWorldClock(t, "2023-09-10"), + query: join([]string{ + `Mtime:yesterday`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-09"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-09 23:59:59.999999999"), + }, + }, }, }, - }, - }, - { - name: "NaturalLanguage DateTimeNode - yesterday - the beginning of the month", - prepare: setWorldClock(t, "2023-09-01"), - givenQuery: join([]string{ - `Mtime:yesterday`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-08-31"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + { + name: "NaturalLanguage DateTimeNode - yesterday - the beginning of the month", + patch: setWorldClock(t, "2023-09-01"), + query: join([]string{ + `Mtime:yesterday`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-31"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + }, + }, }, }, - }, - }, - { - name: "NaturalLanguage DateTimeNode - this week", - prepare: setWorldClock(t, "2023-09-06"), - givenQuery: join([]string{ - `Mtime:"this week"`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-04"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + { + name: "NaturalLanguage DateTimeNode - this week", + patch: setWorldClock(t, "2023-09-06"), + query: join([]string{ + `Mtime:"this week"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-04"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, }, }, - }, - }, - { - name: "NaturalLanguage DateTimeNode - this month", - prepare: setWorldClock(t, "2023-09-02"), - givenQuery: join([]string{ - `Mtime:"this month"`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-30 23:59:59.999999999"), + { + name: "NaturalLanguage DateTimeNode - this month", + patch: setWorldClock(t, "2023-09-02"), + query: join([]string{ + `Mtime:"this month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-30 23:59:59.999999999"), + }, + }, }, }, - }, - }, - { - name: "NaturalLanguage DateTimeNode - last month", - prepare: setWorldClock(t, "2023-09-02"), - givenQuery: join([]string{ - `Mtime:"last month"`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-08-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + { + name: "NaturalLanguage DateTimeNode - last month", + patch: setWorldClock(t, "2023-09-02"), + query: join([]string{ + `Mtime:"last month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + }, + }, }, }, - }, - }, - { - name: "NaturalLanguage DateTimeNode - last month - the beginning of the year", - prepare: setWorldClock(t, "2023-01-01"), - givenQuery: join([]string{ - `Mtime:"last month"`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2022-12-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + { + name: "NaturalLanguage DateTimeNode - last month - the beginning of the year", + patch: setWorldClock(t, "2023-01-01"), + query: join([]string{ + `Mtime:"last month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-12-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + }, + }, }, }, - }, - }, - { - name: "NaturalLanguage DateTimeNode - this year", - prepare: setWorldClock(t, "2023-06-18"), - givenQuery: join([]string{ - `Mtime:"this year"`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-01-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-12-31 23:59:59.999999999"), + { + name: "NaturalLanguage DateTimeNode - this year", + patch: setWorldClock(t, "2023-06-18"), + query: join([]string{ + `Mtime:"this year"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-12-31 23:59:59.999999999"), + }, + }, }, }, - }, - }, - { - name: "NaturalLanguage DateTimeNode - last year", - prepare: setWorldClock(t, "2023-01-01"), - givenQuery: join([]string{ - `Mtime:"last year"`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2022-01-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + { + name: "NaturalLanguage DateTimeNode - last year", + patch: setWorldClock(t, "2023-01-01"), + query: join([]string{ + `Mtime:"last year"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + }, + }, }, }, }, }, { - name: "id", - givenQuery: join([]string{ - `id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, - `ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{ - Key: "id", - Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{ - Key: "ID", - Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", + name: "errors", + cases: []tc{ + { + query: "animal:(mammal:cat mammal:dog reptile:turtle)", + error: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "cat"}, + }, + }, + { + query: "animal:(cat mammal:dog turtle)", + error: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "dog"}, + }, + }, + { + query: "animal:(AND cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: "animal:(OR cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + query: "(AND cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: "(OR cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + query: `cat dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, }, }, }, }, { - name: "animal:(cat dog turtle)", - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "animal", + name: "Random", + cases: []tc{ + { + name: "FullDictionary", + query: join(FullDictionary), + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{ - Value: "cat", + &ast.StringNode{Value: "federated"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "search"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "federat*"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "search"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "search"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fed*"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filename", Value: "budget.xlsx"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "Shakespear"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Paul"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Shakesp*"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "title", Value: "Advanced Search"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "title", Value: "Advanced Sear*"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "title", Value: "Advan* Search"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "title", Value: "*anced Search"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Jane Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{ + Key: "author", + Nodes: []ast.Node{ + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "Jane Smith"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.GroupNode{ + Key: "author", + Nodes: []ast.Node{ + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Jane Smith"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "DepartmentId", Value: "*"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "RelatedHubSites", Value: "*"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "contentclass", Value: "sts_site"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.BooleanNode{Key: "IsHubSite", Value: false}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "filetype", Value: "docx"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "title", Value: "Advanced Search"}, + }, + }, + }, + }, + }, + { + query: join([]string{ + `(name:"moby di*" OR tag:bestseller) AND tag:book NOT tag:read`, + `author:("John Smith" Jane)`, + `author:("John Smith" OR Jane)`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "name", Value: "moby di*"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "tag", Value: "bestseller"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "tag", Value: "book"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Key: "tag", Value: "read"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{ + Key: "author", + Nodes: []ast.Node{ + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "Jane"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.GroupNode{ + Key: "author", + Nodes: []ast.Node{ + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Jane"}, + }, }, + }, + }, + }, + { + query: `author:("John Smith" Jane) author:"Jack" AND author:"Oggy"`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "author", + Nodes: []ast.Node{ + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "Jane"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Jack"}, &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "Oggy"}, + }, + }, + }, + { + query: `author:("John Smith" OR Jane)`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "author", + Nodes: []ast.Node{ + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Jane"}, + }, + }, + }, + }, + }, + { + query: `NOT "John Smith" NOT Jane`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "Jane"}, + }, + }, + }, + { + query: `NOT author:"John Smith" NOT author:"Jane Smith" NOT tag:sifi`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Key: "author", Value: "Jane Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Key: "tag", Value: "sifi"}, + }, + }, + }, + { + query: `scope:"/new folder/subfolder" file`, + ast: &ast.Ast{ + Nodes: []ast.Node{ &ast.StringNode{ - Value: "dog", + Key: "scope", + Value: "/new folder/subfolder", }, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{ - Value: "turtle", + Value: "file", }, }, }, }, - }, - }, - { - name: "(cat dog turtle)", - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ + { + query: ` ๐Ÿ˜‚ "*๐Ÿ˜€ ๐Ÿ˜*" name:๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ๐ŸŽ๐Ÿ˜ name:๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ ๐Ÿ˜`, + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{ - Value: "cat", + Value: "๐Ÿ˜‚", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "*๐Ÿ˜€ ๐Ÿ˜*", }, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{ - Value: "dog", + Key: "name", + Value: "๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ๐ŸŽ๐Ÿ˜", + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{ + Key: "name", + Value: "๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ", }, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{ - Value: "turtle", + Value: "๐Ÿ˜", }, }, }, }, - }, - }, - { - name: "animal:(mammal:cat mammal:dog reptile:turtle)", - expectedError: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "cat"}, - }, - }, - { - name: "animal:(cat mammal:dog turtle)", - expectedError: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "dog"}, - }, - }, - { - name: "animal:(AND cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: "animal:(OR cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: "(AND cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: "(OR cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: `cat dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, + { + query: "animal:(cat dog turtle)", + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "animal", + Nodes: []ast.Node{ + &ast.StringNode{ + Value: "cat", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "dog", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "turtle", + }, + }, + }, + }, + }, }, - }, - }, - { - name: `cat dog fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, + { + query: "(cat dog turtle)", + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{ + Value: "cat", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "dog", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "turtle", + }, + }, + }, + }, + }, }, - }, - }, - { - name: `(cat dog) fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ + { + query: `cat dog fox`, + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, }, }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, }, - }, - }, - { - name: `(mammal:cat mammal:dog) fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ + { + query: `(cat dog) fox`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Key: "mammal", Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "mammal", Value: "dog"}, + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, }, }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, }, - }, - }, - { - name: `mammal:(cat dog) fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "mammal", + { + query: `(mammal:cat mammal:dog) fox`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "mammal", Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "mammal", Value: "dog"}, + }, + }, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, + &ast.StringNode{Value: "fox"}, }, }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, }, - }, - }, - { - name: `mammal:(cat dog) mammal:fox`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "mammal", + { + query: `mammal:(cat dog) fox`, + ast: &ast.Ast{ Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, + &ast.GroupNode{ + Key: "mammal", + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, + &ast.StringNode{Value: "fox"}, }, }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "mammal", Value: "fox"}, }, - }, - }, - { - name: `title:((Advanced OR Search OR Query) -"Advanced Search Query")`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "title", + { + query: `mammal:(cat dog) mammal:fox`, + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ + Key: "mammal", Nodes: []ast.Node{ - &ast.StringNode{Value: "Advanced"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Search"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Query"}, + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, }, }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "Advanced Search Query"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "mammal", Value: "fox"}, + }, + }, + }, + { + query: `title:((Advanced OR Search OR Query) -"Advanced Search Query")`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "title", + Nodes: []ast.Node{ + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "Advanced"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Search"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Query"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "Advanced Search Query"}, + }, + }, + }, + }, + }, + { + query: join([]string{ + `id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, + `ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{ + Key: "id", + Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{ + Key: "ID", + Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", + }, }, }, }, @@ -1243,43 +1267,59 @@ func TestParse(t *testing.T) { }, } - assert := tAssert.New(t) + for _, currentTest := range tests { + currentTest := currentTest - for _, tt := range tests { - tt := tt + t.Run( + currentTest.name, + func(t *testing.T) { + for i, testCase := range currentTest.cases { + currentTest := currentTest + subTestName := strconv.Itoa(i) - t.Run(tt.name, func(t *testing.T) { - if tt.skip { - t.Skip() - } + if testCase.query != "" { + subTestName = subTestName + " - " + testCase.query + } - if tt.prepare != nil { - prepare := tt.prepare() - defer prepare() - } + if testCase.name != "" { + subTestName = testCase.name + } - q := tt.name + t.Run(subTestName, func(t *testing.T) { + if testCase.skip { + t.Skip() + } - if tt.givenQuery != "" { - q = tt.givenQuery - } + if testCase.patch != nil { + revert := testCase.patch() + defer revert() + } - parsedAST, err := kql.Builder{}.Build(q) + query := currentTest.name + if testCase.query != "" { + query = testCase.query + } - if tt.expectedError != nil { - if tt.expectedError.Error() != "" { - assert.Equal(err.Error(), tt.expectedError.Error()) - } else { - assert.NotNil(err) - } + astResult, err := kql.Builder{}.Build(query) + assert := tAssert.New(t) - return - } + if testCase.error != nil { + if expectedError := testCase.error.Error(); expectedError != "" { + assert.Equal(err.Error(), expectedError) + } else { + assert.NotNil(err) + } - if diff := test.DiffAst(tt.expectedAst, parsedAST); diff != "" { - t.Fatalf("AST mismatch \nquery: '%s' \n(-expected +got): %s", q, diff) - } - }) + return + } + + if diff := test.DiffAst(testCase.ast, astResult); diff != "" { + t.Fatalf("AST mismatch \nquery: '%s' \n(-expected +got): %s", query, diff) + } + }) + } + }, + ) } } From 6eea04987b4b0505202558b77e66922cc64eed17 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Fri, 15 Sep 2023 09:27:43 +0200 Subject: [PATCH 11/11] fix: time-range error naming --- services/search/pkg/query/kql/cast.go | 4 +- .../search/pkg/query/kql/dictionary_test.go | 2320 +++++++++-------- services/search/pkg/query/kql/error.go | 11 +- 3 files changed, 1178 insertions(+), 1157 deletions(-) diff --git a/services/search/pkg/query/kql/cast.go b/services/search/pkg/query/kql/cast.go index 60c42040898..e3b9ab58106 100644 --- a/services/search/pkg/query/kql/cast.go +++ b/services/search/pkg/query/kql/cast.go @@ -84,7 +84,7 @@ func toTimeRange(in interface{}) (*time.Time, *time.Time, error) { value, err := toString(in) if err != nil { - return &from, &to, UnsupportedTimeRange{} + return &from, &to, UnsupportedTimeRangeError{} } c := &now.Config{ @@ -121,7 +121,7 @@ func toTimeRange(in interface{}) (*time.Time, *time.Time, error) { } if from.IsZero() || to.IsZero() { - return nil, nil, UnsupportedTimeRange{} + return nil, nil, UnsupportedTimeRangeError{} } return &from, &to, nil diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index 34edf4e230e..05e3cf56135 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -1,7 +1,6 @@ package kql_test import ( - "strconv" "strings" "testing" "time" @@ -14,1320 +13,1345 @@ import ( "github.com/owncloud/ocis/v2/services/search/pkg/query/kql" ) -var mustParseTime = func(t *testing.T, ts string) time.Time { - tp, err := now.Parse(ts) - if err != nil { - t.Fatalf("time.Parse(...) error = %v", err) - } - - return tp -} - -var setWorldClock = func(t *testing.T, ts string) func() func() { - return func() func() { - kql.PatchTimeNow(func() time.Time { - return mustParseTime(t, ts) - }) - - return func() { - kql.PatchTimeNow(time.Now) - } - } -} - -var patchNow = func(t *testing.T, ts string) func() func() { - return func() func() { - kql.PatchTimeNow(func() time.Time { - return mustParseTime(t, ts) - }) - - return func() { - kql.PatchTimeNow(time.Now) - } - } -} - -var join = func(v []string) string { - return strings.Join(v, " ") -} - -var FullDictionary = []string{ - `federated search`, - `federat* search`, - `search fed*`, - `author:"John Smith"`, - `filetype:docx`, - `filename:budget.xlsx`, - `author: "John Smith"`, - `author :"John Smith"`, - `author : "John Smith"`, - `author "John Smith"`, - `author "John Smith"`, - `author:Shakespear`, - `author:Paul`, - `author:Shakesp*`, - `title:"Advanced Search"`, - `title:"Advanced Sear*"`, - `title:"Advan* Search"`, - `title:"*anced Search"`, - `author:"John Smith" OR author:"Jane Smith"`, - `author:"John Smith" AND filetype:docx`, - `author:("John Smith" "Jane Smith")`, - `author:("John Smith" OR "Jane Smith")`, - `(DepartmentId:* OR RelatedHubSites:*) AND contentclass:sts_site NOT IsHubSite:false`, - `author:"John Smith" (filetype:docx title:"Advanced Search")`, -} - -func TestParse(t *testing.T) { - type tc struct { - name string - query string - ast *ast.Ast - error error - skip bool - patch func() func() - } - - tests := []struct { - name string - cases []tc - }{ - { - // SPEC ////////////////////////////////////////////////////////////////////////////// - // - // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf - // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 - // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference - name: `spec`, - cases: []tc{ - // 2.1.2 AND Operator - // 3.1.2 AND Operator - { - query: `cat AND dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - query: `AND`, - error: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - query: `AND cat AND dog`, - error: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - // 2.1.6 NOT Operator - // 3.1.6 NOT Operator - { - query: `cat NOT dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - query: `NOT dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - // 2.1.8 OR Operator - // 3.1.8 OR Operator - { - query: `cat OR dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - query: `OR`, - error: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - query: `OR cat AND dog`, - error: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - // 3.1.11 Implicit Operator - { - query: `cat dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, - { - query: `cat AND (dog OR fox)`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "fox"}, - }}, - }, - }, - }, - { - query: `cat (dog OR fox)`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "fox"}, - }}, - }, - }, - }, - // 2.1.12 Parentheses - // 3.1.12 Parentheses - { - query: `(cat OR dog) AND fox`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "dog"}, - }}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, - }, - }, - }, - // 3.2.3 Implicit Operator for Property Restriction - { - query: `author:"John Smith" filetype:docx`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - }, - }, - }, - { - query: `author:"John Smith" AND filetype:docx`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - }, - }, +func TestParse_Spec(t *testing.T) { + // SPEC ////////////////////////////////////////////////////////////////////////////// + // + // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf + // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 + // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference + tests := []testCase{ + // 2.1.2 AND Operator + // 3.1.2 AND Operator + { + name: `cat AND dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, }, - { - query: `author:"John Smith" author:"Jane Smith"`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Jane Smith"}, - }, - }, + }, + }, + { + name: `AND`, + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + name: `AND cat AND dog`, + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + // 2.1.6 NOT Operator + // 3.1.6 NOT Operator + { + name: `cat NOT dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, }, - { - query: `author:"John Smith" OR author:"Jane Smith"`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Jane Smith"}, - }, - }, + }, + }, + { + name: `NOT dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, }, - { - query: `cat filetype:docx`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - }, - }, + }, + }, + // 2.1.8 OR Operator + // 3.1.8 OR Operator + { + name: `cat OR dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "dog"}, }, - { - query: `cat AND filetype:docx`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - }, - }, + }, + }, + { + name: `OR`, + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + name: `OR cat AND dog`, + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + // 3.1.11 Implicit Operator + { + name: `cat dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, }, - // 3.3.1.1.1 Implicit AND Operator - { - query: `cat +dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, + }, + }, + { + name: `cat AND (dog OR fox)`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "fox"}, + }}, }, - { - query: `cat AND dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, + }, + }, + { + name: `cat (dog OR fox)`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "fox"}, + }}, }, - { - query: `cat -dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, - }, - }, + }, + }, + // 2.1.12 Parentheses + // 3.1.12 Parentheses + { + name: `(cat OR dog) AND fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "dog"}, + }}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, }, - { - query: `cat AND NOT dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "dog"}, - }, - }, + }, + }, + // 3.2.3 Implicit Operator for Property Restriction + { + name: `author:"John Smith" filetype:docx`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, }, - { - query: `cat +dog -fox`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, - }, - }, + }, + }, + { + name: `author:"John Smith" AND filetype:docx`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, }, - { - query: `cat AND dog AND NOT fox`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, - }, - }, + }, + }, + { + name: `author:"John Smith" author:"Jane Smith"`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Jane Smith"}, }, - { - query: `cat dog +fox`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, - }, - }, + }, + }, + { + name: `author:"John Smith" OR author:"Jane Smith"`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Jane Smith"}, }, - { - query: `fox OR (fox AND (cat OR dog))`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "fox"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "fox"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "dog"}, - }}, - }}, - }, - }, + }, + }, + { + name: `cat filetype:docx`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, }, - { - query: `cat dog -fox`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, - }, - }, + }, + }, + { + name: `cat AND filetype:docx`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, }, - { - query: `(NOT fox) AND (cat OR dog)`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, - }}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "dog"}, - }}, - }, - }, + }, + }, + // 3.3.1.1.1 Implicit AND Operator + { + name: `cat +dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, }, - { - query: `cat +dog -fox`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, - }, - }, + }, + }, + { + name: `cat AND dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, }, - { - query: `(NOT fox) AND (dog OR (dog AND cat))`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Value: "fox"}, - }}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.GroupNode{Nodes: []ast.Node{ - &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "cat"}, - }}, - }}, - }, - }, + }, + }, + { + name: `cat -dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, }, - // 2.3.5 Date Tokens - // 3.3.5 Date Tokens - { - query: `Modified:2023-09-05`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05"), - }, - }, - }, + }, + }, + { + name: `cat AND NOT dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "dog"}, }, - { - query: `Modified:"2008-01-29"`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2008-01-29"), - }, - }, - }, + }, + }, + { + name: `cat +dog -fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, }, - { - query: `Modified:today`, - patch: patchNow(t, "2023-09-10"), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-10"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Modified", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), - }, - }, - }, + }, + }, + { + name: `cat AND dog AND NOT fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, }, }, }, { - name: "DateTimeRestrictionNode", - cases: []tc{ - { - query: join([]string{ - `Mtime:"2023-09-05T08:42:11.23554+02:00"`, - `Mtime:2023-09-05T08:42:11.23554+02:00`, - `Mtime="2023-09-05T08:42:11.23554+02:00"`, - `Mtime=2023-09-05T08:42:11.23554+02:00`, - `Mtime<"2023-09-05T08:42:11.23554+02:00"`, - `Mtime<2023-09-05T08:42:11.23554+02:00`, - `Mtime<="2023-09-05T08:42:11.23554+02:00"`, - `Mtime<=2023-09-05T08:42:11.23554+02:00`, - `Mtime>"2023-09-05T08:42:11.23554+02:00"`, - `Mtime>2023-09-05T08:42:11.23554+02:00`, - `Mtime>="2023-09-05T08:42:11.23554+02:00"`, - `Mtime>=2023-09-05T08:42:11.23554+02:00`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, + name: `cat dog +fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + name: `fox OR (fox AND (cat OR dog))`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "fox"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "fox"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - }, - }, + &ast.StringNode{Value: "dog"}, + }}, + }}, }, - { - name: "NaturalLanguage DateTimeNode - today", - patch: setWorldClock(t, "2023-09-10"), - query: join([]string{ - `Mtime:today`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-10"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), - }, - }, - }, + }, + }, + { + name: `cat dog -fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, }, - { - name: "NaturalLanguage DateTimeNode - yesterday", - patch: setWorldClock(t, "2023-09-10"), - query: join([]string{ - `Mtime:yesterday`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-09"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-09 23:59:59.999999999"), - }, - }, - }, + }, + }, + { + name: `(NOT fox) AND (cat OR dog)`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, + }}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "dog"}, + }}, }, - { - name: "NaturalLanguage DateTimeNode - yesterday - the beginning of the month", - patch: setWorldClock(t, "2023-09-01"), - query: join([]string{ - `Mtime:yesterday`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-08-31"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), - }, - }, - }, + }, + }, + { + name: `cat +dog -fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, }, - { - name: "NaturalLanguage DateTimeNode - this week", - patch: setWorldClock(t, "2023-09-06"), - query: join([]string{ - `Mtime:"this week"`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-04"), - }, + }, + }, + { + name: `(NOT fox) AND (dog OR (dog AND cat))`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "fox"}, + }}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Value: "dog"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), - }, - }, + &ast.StringNode{Value: "cat"}, + }}, + }}, + }, + }, + }, + // 2.3.5 Date Tokens + // 3.3.5 Date Tokens + { + name: `Modified:2023-09-05`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05"), }, }, - { - name: "NaturalLanguage DateTimeNode - this month", - patch: setWorldClock(t, "2023-09-02"), - query: join([]string{ - `Mtime:"this month"`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-30 23:59:59.999999999"), - }, - }, + }, + }, + { + name: `Modified:"2008-01-29"`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2008-01-29"), }, }, - { - name: "NaturalLanguage DateTimeNode - last month", - patch: setWorldClock(t, "2023-09-02"), - query: join([]string{ - `Mtime:"last month"`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-08-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), - }, - }, + }, + }, + { + name: `Modified:today`, + patch: patchNow(t, "2023-09-10"), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), }, }, - { - name: "NaturalLanguage DateTimeNode - last month - the beginning of the year", - patch: setWorldClock(t, "2023-01-01"), - query: join([]string{ - `Mtime:"last month"`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2022-12-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), - }, - }, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + testKQL(t, tc) + }) + } +} + +func TestParse_DateTimeRestrictionNode(t *testing.T) { + tests := []testCase{ + { + name: "format", + query: join([]string{ + `Mtime:"2023-09-05T08:42:11.23554+02:00"`, + `Mtime:2023-09-05T08:42:11.23554+02:00`, + `Mtime="2023-09-05T08:42:11.23554+02:00"`, + `Mtime=2023-09-05T08:42:11.23554+02:00`, + `Mtime<"2023-09-05T08:42:11.23554+02:00"`, + `Mtime<2023-09-05T08:42:11.23554+02:00`, + `Mtime<="2023-09-05T08:42:11.23554+02:00"`, + `Mtime<=2023-09-05T08:42:11.23554+02:00`, + `Mtime>"2023-09-05T08:42:11.23554+02:00"`, + `Mtime>2023-09-05T08:42:11.23554+02:00`, + `Mtime>="2023-09-05T08:42:11.23554+02:00"`, + `Mtime>=2023-09-05T08:42:11.23554+02:00`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), }, }, - { - name: "NaturalLanguage DateTimeNode - this year", - patch: setWorldClock(t, "2023-06-18"), - query: join([]string{ - `Mtime:"this year"`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-01-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-12-31 23:59:59.999999999"), - }, - }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - today", + patch: setWorldClock(t, "2023-09-10"), + query: join([]string{ + `Mtime:today`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), }, }, - { - name: "NaturalLanguage DateTimeNode - last year", - patch: setWorldClock(t, "2023-01-01"), - query: join([]string{ - `Mtime:"last year"`, - }), - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2022-01-01"), - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), - }, - }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - yesterday", + patch: setWorldClock(t, "2023-09-10"), + query: join([]string{ + `Mtime:yesterday`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-09"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-09 23:59:59.999999999"), }, }, }, }, { - name: "errors", - cases: []tc{ - { - query: "animal:(mammal:cat mammal:dog reptile:turtle)", - error: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "cat"}, + name: "NaturalLanguage DateTimeNode - yesterday - the beginning of the month", + patch: setWorldClock(t, "2023-09-01"), + query: join([]string{ + `Mtime:yesterday`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-31"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), }, }, - { - query: "animal:(cat mammal:dog turtle)", - error: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "dog"}, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this week", + patch: setWorldClock(t, "2023-09-06"), + query: join([]string{ + `Mtime:"this week"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-04"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), }, }, - { - query: "animal:(AND cat)", - error: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this month", + patch: setWorldClock(t, "2023-09-02"), + query: join([]string{ + `Mtime:"this month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-30 23:59:59.999999999"), }, }, - { - query: "animal:(OR cat)", - error: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last month", + patch: setWorldClock(t, "2023-09-02"), + query: join([]string{ + `Mtime:"last month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), }, }, - { - query: "(AND cat)", - error: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last month - the beginning of the year", + patch: setWorldClock(t, "2023-01-01"), + query: join([]string{ + `Mtime:"last month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-12-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), }, }, - { - query: "(OR cat)", - error: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this year", + patch: setWorldClock(t, "2023-06-18"), + query: join([]string{ + `Mtime:"this year"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-12-31 23:59:59.999999999"), }, }, - { - query: `cat dog`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last year", + patch: setWorldClock(t, "2023-01-01"), + query: join([]string{ + `Mtime:"last year"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), }, }, }, }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + testKQL(t, tc) + }) + } +} + +func TestParse_Errors(t *testing.T) { + tests := []testCase{ + { + query: "animal:(mammal:cat mammal:dog reptile:turtle)", + error: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "cat"}, + }, + }, + { + query: "animal:(cat mammal:dog turtle)", + error: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "dog"}, + }, + }, + { + query: "animal:(AND cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: "animal:(OR cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + query: "(AND cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: "(OR cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + query: `cat dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + testKQL(t, tc) + }) + } +} + +func TestParse_Stress(t *testing.T) { + tests := []testCase{ { - name: "Random", - cases: []tc{ - { - name: "FullDictionary", - query: join(FullDictionary), - ast: &ast.Ast{ + name: "FullDictionary", + query: join(FullDictionary), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "federated"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "search"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "federat*"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "search"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "search"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fed*"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filename", Value: "budget.xlsx"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "author"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "Shakespear"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Paul"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Shakesp*"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "title", Value: "Advanced Search"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "title", Value: "Advanced Sear*"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "title", Value: "Advan* Search"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "title", Value: "*anced Search"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Jane Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "filetype", Value: "docx"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{ + Key: "author", Nodes: []ast.Node{ - &ast.StringNode{Value: "federated"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "federat*"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "search"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fed*"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filename", Value: "budget.xlsx"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "author"}, - &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "Jane Smith"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.GroupNode{ + Key: "author", + Nodes: []ast.Node{ &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "Shakespear"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Paul"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Shakesp*"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "title", Value: "Advanced Search"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "title", Value: "Advanced Sear*"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "title", Value: "Advan* Search"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "title", Value: "*anced Search"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Jane Smith"}, &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "filetype", Value: "docx"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{ - Key: "author", - Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "Jane Smith"}, - }, - }, + &ast.StringNode{Value: "Jane Smith"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "DepartmentId", Value: "*"}, &ast.OperatorNode{Value: kql.BoolOR}, - &ast.GroupNode{ - Key: "author", - Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Jane Smith"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "DepartmentId", Value: "*"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "RelatedHubSites", Value: "*"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "contentclass", Value: "sts_site"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.BooleanNode{Key: "IsHubSite", Value: false}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "filetype", Value: "docx"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "title", Value: "Advanced Search"}, - }, - }, + &ast.StringNode{Key: "RelatedHubSites", Value: "*"}, }, }, - }, - { - query: join([]string{ - `(name:"moby di*" OR tag:bestseller) AND tag:book NOT tag:read`, - `author:("John Smith" Jane)`, - `author:("John Smith" OR Jane)`, - }), - ast: &ast.Ast{ + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "contentclass", Value: "sts_site"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.BooleanNode{Key: "IsHubSite", Value: false}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{ Nodes: []ast.Node{ - &ast.GroupNode{ - Nodes: []ast.Node{ - &ast.StringNode{Key: "name", Value: "moby di*"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "tag", Value: "bestseller"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "tag", Value: "book"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Key: "tag", Value: "read"}, + &ast.StringNode{Key: "filetype", Value: "docx"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.GroupNode{ - Key: "author", - Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "Jane"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.GroupNode{ - Key: "author", - Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Jane"}, - }, - }, + &ast.StringNode{Key: "title", Value: "Advanced Search"}, }, }, }, - { - query: `author:("John Smith" Jane) author:"Jack" AND author:"Oggy"`, - ast: &ast.Ast{ + }, + }, + { + name: "complex", + query: join([]string{ + `(name:"moby di*" OR tag:bestseller) AND tag:book NOT tag:read`, + `author:("John Smith" Jane)`, + `author:("John Smith" OR Jane)`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "author", - Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "Jane"}, - }, - }, + &ast.StringNode{Key: "name", Value: "moby di*"}, &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "author", Value: "Jack"}, + &ast.StringNode{Key: "tag", Value: "bestseller"}, + }, + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "tag", Value: "book"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Key: "tag", Value: "read"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.GroupNode{ + Key: "author", + Nodes: []ast.Node{ + &ast.StringNode{Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Key: "author", Value: "Oggy"}, + &ast.StringNode{Value: "Jane"}, }, }, - }, - { - query: `author:("John Smith" OR Jane)`, - ast: &ast.Ast{ + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.GroupNode{ + Key: "author", Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "author", - Nodes: []ast.Node{ - &ast.StringNode{Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Value: "Jane"}, - }, - }, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Jane"}, }, }, }, - { - query: `NOT "John Smith" NOT Jane`, - ast: &ast.Ast{ + }, + }, + { + name: `author:("John Smith" Jane) author:"Jack" AND author:"Oggy"`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "author", Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, &ast.StringNode{Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, &ast.StringNode{Value: "Jane"}, }, }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "author", Value: "Jack"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Key: "author", Value: "Oggy"}, }, - { - query: `NOT author:"John Smith" NOT author:"Jane Smith" NOT tag:sifi`, - ast: &ast.Ast{ + }, + }, + { + name: `author:("John Smith" OR Jane)`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "author", Nodes: []ast.Node{ - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Key: "author", Value: "John Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Key: "author", Value: "Jane Smith"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.OperatorNode{Value: kql.BoolNOT}, - &ast.StringNode{Key: "tag", Value: "sifi"}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Jane"}, }, }, }, - { - query: `scope:"/new folder/subfolder" file`, - ast: &ast.Ast{ + }, + }, + { + name: `NOT "John Smith" NOT Jane`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "Jane"}, + }, + }, + }, + { + name: `NOT author:"John Smith" NOT author:"Jane Smith" NOT tag:sifi`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Key: "author", Value: "John Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Key: "author", Value: "Jane Smith"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Key: "tag", Value: "sifi"}, + }, + }, + }, + { + name: `scope:"/new folder/subfolder" file`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{ + Key: "scope", + Value: "/new folder/subfolder", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "file", + }, + }, + }, + }, + { + name: ` ๐Ÿ˜‚ "*๐Ÿ˜€ ๐Ÿ˜*" name:๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ๐ŸŽ๐Ÿ˜ name:๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ ๐Ÿ˜`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{ + Value: "๐Ÿ˜‚", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "*๐Ÿ˜€ ๐Ÿ˜*", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Key: "name", + Value: "๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ๐ŸŽ๐Ÿ˜", + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{ + Key: "name", + Value: "๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "๐Ÿ˜", + }, + }, + }, + }, + { + name: "animal:(cat dog turtle)", + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "animal", Nodes: []ast.Node{ &ast.StringNode{ - Key: "scope", - Value: "/new folder/subfolder", + Value: "cat", }, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{ - Value: "file", + Value: "dog", + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{ + Value: "turtle", }, }, }, }, - { - query: ` ๐Ÿ˜‚ "*๐Ÿ˜€ ๐Ÿ˜*" name:๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ๐ŸŽ๐Ÿ˜ name:๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ ๐Ÿ˜`, - ast: &ast.Ast{ + }, + }, + { + name: "(cat dog turtle)", + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ Nodes: []ast.Node{ &ast.StringNode{ - Value: "๐Ÿ˜‚", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "*๐Ÿ˜€ ๐Ÿ˜*", + Value: "cat", }, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{ - Key: "name", - Value: "๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ๐ŸŽ๐Ÿ˜", - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{ - Key: "name", - Value: "๐Ÿ˜‚๐Ÿ’๐Ÿ‘Œ", + Value: "dog", }, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{ - Value: "๐Ÿ˜", + Value: "turtle", }, }, }, }, - { - query: "animal:(cat dog turtle)", - ast: &ast.Ast{ + }, + }, + { + name: `cat dog fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, + }, + }, + }, + { + name: `(cat dog) fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "animal", - Nodes: []ast.Node{ - &ast.StringNode{ - Value: "cat", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "dog", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "turtle", - }, - }, - }, + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, }, }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, }, - { - query: "(cat dog turtle)", - ast: &ast.Ast{ + }, + }, + { + name: `(mammal:cat mammal:dog) fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ Nodes: []ast.Node{ - &ast.GroupNode{ - Nodes: []ast.Node{ - &ast.StringNode{ - Value: "cat", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "dog", - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{ - Value: "turtle", - }, - }, - }, + &ast.StringNode{Key: "mammal", Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "mammal", Value: "dog"}, }, }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, }, - { - query: `cat dog fox`, - ast: &ast.Ast{ + }, + }, + { + name: `mammal:(cat dog) fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "mammal", Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, &ast.StringNode{Value: "dog"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, }, }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "fox"}, }, - { - query: `(cat dog) fox`, - ast: &ast.Ast{ + }, + }, + { + name: `mammal:(cat dog) mammal:fox`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "mammal", Nodes: []ast.Node{ - &ast.GroupNode{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, + &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, + &ast.StringNode{Value: "dog"}, }, }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Key: "mammal", Value: "fox"}, }, - { - query: `(mammal:cat mammal:dog) fox`, - ast: &ast.Ast{ + }, + }, + { + name: `title:((Advanced OR Search OR Query) -"Advanced Search Query")`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "title", Nodes: []ast.Node{ &ast.GroupNode{ Nodes: []ast.Node{ - &ast.StringNode{Key: "mammal", Value: "cat"}, + &ast.StringNode{Value: "Advanced"}, &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "mammal", Value: "dog"}, + &ast.StringNode{Value: "Search"}, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{Value: "Query"}, }, }, &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, + &ast.OperatorNode{Value: kql.BoolNOT}, + &ast.StringNode{Value: "Advanced Search Query"}, }, }, }, - { - query: `mammal:(cat dog) fox`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "mammal", - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "fox"}, - }, + }, + }, + { + name: "ids", + query: join([]string{ + `id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, + `ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{ + Key: "id", + Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{ + Key: "ID", + Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", }, }, - { - query: `mammal:(cat dog) mammal:fox`, - ast: &ast.Ast{ - Nodes: []ast.Node{ - &ast.GroupNode{ - Key: "mammal", - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{Key: "mammal", Value: "fox"}, - }, + }, + }, + { + name: `"test:test" test:"test:test" "more:*+#!/ยฐ^ยง$%&&/()=?<><<<<