···1212// See the License for the specific language governing permissions and
1313// limitations under the License.
14141515-package zoekt
1515+package query
16161717import (
1818 "bytes"
···2222)
23232424var _ = log.Printf
2525+2626+const ngramSize = 3
25272628type SuggestQueryError struct {
2729 Message string
···151153 var current []byte
152154 add := func(q Query) {
153155 if negate {
154154- q = &NotQuery{q}
156156+ q = &Not{q}
155157 }
156158 qs = append(qs, q)
157159 negate = false
···179181 if fn, n, ok, err := tryConsumeFile(b); err != nil {
180182 return nil, err
181183 } else if ok {
182182- add(&SubstringQuery{
184184+ add(&Substring{
183185 Pattern: fn,
184186 FileName: true,
185187 })
···190192 if fn, n, ok, err := tryConsumeRepo(b); err != nil {
191193 return nil, err
192194 } else if ok {
193193- add(&RepoQuery{Name: fn})
195195+ add(&Repo{Name: fn})
194196 b = b[n:]
195197 continue
196198 }
···198200 if fn, n, ok, err := tryConsumeBranch(b); err != nil {
199201 return nil, err
200202 } else if ok {
201201- add(&BranchQuery{
203203+ add(&Branch{
202204 Name: fn,
203205 })
204206 b = b[n:]
···213215 return nil, err
214216 }
215217216216- substrQ := regexpToQuery(r)
217217- if v, ok := isConst(substrQ); ok && v {
218218+ substrQ := RegexpToQuery(r)
219219+ if v, ok := substrQ.(*Const); ok && v.Value {
218220 return nil, fmt.Errorf("regexp %s is too general. Need at least %d consecutive characters", arg, ngramSize)
219221 }
220222221221- add(&RegexpQuery{
223223+ add(&Regexp{
222224 Regexp: r,
223225 })
224226 b = b[n:]
···240242 if isSpace(c) {
241243 inWord = false
242244 if len(current) > 0 {
243243- add(&SubstringQuery{Pattern: string(current)})
245245+ add(&Substring{Pattern: string(current)})
244246 current = current[:0]
245247 }
246248 b = b[1:]
···253255 }
254256255257 if len(current) > 0 {
256256- add(&SubstringQuery{Pattern: string(current)})
258258+ add(&Substring{Pattern: string(current)})
257259 }
258260259261 for _, q := range qs {
260260- if sq, ok := q.(*SubstringQuery); ok {
262262+ if sq, ok := q.(*Substring); ok {
261263 if len(sq.Pattern) < 3 {
262264 return nil, &SuggestQueryError{
263265 fmt.Sprintf("pattern %q too short", sq.Pattern),
···283285 return qs[0], nil
284286 }
285287286286- return &AndQuery{qs}, nil
288288+ return &And{qs}, nil
287289}
-82
parse_test.go
···11-// Copyright 2016 Google Inc. All rights reserved.
22-//
33-// Licensed under the Apache License, Version 2.0 (the "License");
44-// you may not use this file except in compliance with the License.
55-// You may obtain a copy of the License at
66-//
77-// http://www.apache.org/licenses/LICENSE-2.0
88-//
99-// Unless required by applicable law or agreed to in writing, software
1010-// distributed under the License is distributed on an "AS IS" BASIS,
1111-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212-// See the License for the specific language governing permissions and
1313-// limitations under the License.
1414-1515-package zoekt
1616-1717-import (
1818- "reflect"
1919- "regexp/syntax"
2020- "testing"
2121-)
2222-2323-func mustParseRE(s string) *syntax.Regexp {
2424- r, err := syntax.Parse(s, 0)
2525- if err != nil {
2626- panic(err)
2727- }
2828-2929- return r
3030-}
3131-3232-func TestParseQuery(t *testing.T) {
3333- type testcase struct {
3434- in string
3535- out Query
3636- hasErr bool
3737- }
3838-3939- for _, c := range []testcase{
4040- {"sub-pixel", &SubstringQuery{Pattern: "sub-pixel"}, false},
4141- {"abc", &SubstringQuery{Pattern: "abc"}, false},
4242- {"\"abc bcd\"", &SubstringQuery{Pattern: "abc bcd"}, false},
4343- {"abc bcd", &AndQuery{[]Query{
4444- &SubstringQuery{Pattern: "abc"},
4545- &SubstringQuery{Pattern: "bcd"},
4646- }}, false},
4747- {"-abc", &NotQuery{&SubstringQuery{Pattern: "abc"}}, false},
4848- {"regex:a.b", nil, true},
4949-5050- {"abccase:yes", &SubstringQuery{Pattern: "abccase:yes"}, false},
5151- {"file:abc", &SubstringQuery{Pattern: "abc", FileName: true}, false},
5252- {"branch:pqr", &BranchQuery{Name: "pqr"}, false},
5353-5454- {"file:helpers.go byte", &AndQuery{[]Query{
5555- &SubstringQuery{Pattern: "helpers.go", FileName: true},
5656- &SubstringQuery{Pattern: "byte"},
5757- }}, false},
5858-5959- {"regex:abc[p-q]", &RegexpQuery{mustParseRE("abc[p-q]")}, false},
6060- {"repo:go", &RepoQuery{"go"}, false},
6161-6262- // case
6363- {"abc case:yes", &SubstringQuery{Pattern: "abc", CaseSensitive: true}, false},
6464- {"abc case:auto", &SubstringQuery{Pattern: "abc", CaseSensitive: false}, false},
6565- {"ABC case:auto", &SubstringQuery{Pattern: "ABC", CaseSensitive: true}, false},
6666- {"ABC case:\"auto\"", &SubstringQuery{Pattern: "ABC", CaseSensitive: true}, false},
6767- // errors.
6868- {"\"abc", nil, true},
6969- {"\"a\\", nil, true},
7070- {"case:foo", nil, true},
7171- {"", nil, true},
7272- } {
7373- q, err := Parse(c.in)
7474- if c.hasErr != (err != nil) {
7575- t.Errorf("Parse(%s): error %v, value %v", c.in, err, q)
7676- } else if q != nil {
7777- if !reflect.DeepEqual(q, c.out) {
7878- t.Errorf("Parse(%s): got %v want %v", c.in, q, c.out)
7979- }
8080- }
8181- }
8282-}
+54-72
query.go
query/query.go
···1212// See the License for the specific language governing permissions and
1313// limitations under the License.
14141515-package zoekt
1515+package query
16161717import (
1818 "fmt"
···3030}
31313232// RegexpQuery is a query looking for regular expressions matches.
3333-type RegexpQuery struct {
3333+type Regexp struct {
3434 Regexp *syntax.Regexp
3535}
36363737-func (q *RegexpQuery) String() string {
3737+func (q *Regexp) String() string {
3838 return fmt.Sprintf("regex:%q", q.Regexp.String())
3939}
40404141-type TrueQuery struct{}
4242-4343-func (q *TrueQuery) String() string {
4444- return "TRUE"
4141+type Const struct {
4242+ Value bool
4543}
46444747-type FalseQuery struct{}
4848-4949-func (q *FalseQuery) String() string {
4545+func (q *Const) String() string {
4646+ if q.Value {
4747+ return "TRUE"
4848+ }
5049 return "FALSE"
5150}
52515353-type RepoQuery struct {
5252+type Repo struct {
5453 Name string
5554}
56555757-func (q *RepoQuery) String() string {
5656+func (q *Repo) String() string {
5857 return fmt.Sprintf("repo:%s", q.Name)
5958}
60596161-// SubstringQuery is the most basic query: a query for a substring.
6262-type SubstringQuery struct {
6060+// Substring is the most basic query: a query for a substring.
6161+type Substring struct {
6362 Pattern string
6463 CaseSensitive bool
6564 FileName bool
6665}
67666868-func (q *SubstringQuery) String() string {
6767+func (q *Substring) String() string {
6968 s := ""
70697170 t := "sub"
···8079 return s
8180}
82818383-// OrQuery is matched when any of its children is matched.
8484-type OrQuery struct {
8282+// Or is matched when any of its children is matched.
8383+type Or struct {
8584 Children []Query
8685}
87868888-func (q *OrQuery) String() string {
8787+func (q *Or) String() string {
8988 var sub []string
9089 for _, ch := range q.Children {
9190 sub = append(sub, ch.String())
···9392 return fmt.Sprintf("(or %s)", strings.Join(sub, " "))
9493}
95949696-// NotQuery inverts the meaning of its child.
9797-type NotQuery struct {
9595+// Not inverts the meaning of its child.
9696+type Not struct {
9897 Child Query
9998}
10099101101-func (q *NotQuery) String() string {
100100+func (q *Not) String() string {
102101 return fmt.Sprintf("(not %s)", q.Child)
103102}
104103105105-// AndQuery is matched when all its children are.
106106-type AndQuery struct {
104104+// And is matched when all its children are.
105105+type And struct {
107106 Children []Query
108107}
109108110110-func (q *AndQuery) String() string {
109109+func (q *And) String() string {
111110 var sub []string
112111 for _, ch := range q.Children {
113112 sub = append(sub, ch.String())
···115114 return fmt.Sprintf("(and %s)", strings.Join(sub, " "))
116115}
117116118118-// BranchQuery limits search to a specific branch.
119119-type BranchQuery struct {
117117+// Branch limits search to a specific branch.
118118+type Branch struct {
120119 Name string
121120}
122121123123-func (q *BranchQuery) String() string {
122122+func (q *Branch) String() string {
124123 return fmt.Sprintf("branch:%q", q.Name)
125124}
126125127126func queryChildren(q Query) []Query {
128127 switch s := q.(type) {
129129- case *AndQuery:
128128+ case *And:
130129 return s.Children
131131- case *OrQuery:
130130+ case *Or:
132131 return s.Children
133132 }
134133 return nil
···157156// (and (and x y) z) => (and x y z) , the same for "or"
158157func flatten(q Query) (Query, bool) {
159158 switch s := q.(type) {
160160- case *AndQuery:
159159+ case *And:
161160 if len(s.Children) == 1 {
162161 return s.Children[0], true
163162 }
164163 flatChildren, changed := flattenAndOr(s.Children, s)
165165- return &AndQuery{flatChildren}, changed
166166- case *OrQuery:
164164+ return &And{flatChildren}, changed
165165+ case *Or:
167166 if len(s.Children) == 1 {
168167 return s.Children[0], true
169168 }
170169 flatChildren, changed := flattenAndOr(s.Children, s)
171171- return &OrQuery{flatChildren}, changed
170170+ return &Or{flatChildren}, changed
172171 default:
173172 return q, false
174173 }
···182181 return neg
183182}
184183185185-func constQuery(c bool) Query {
186186- if c {
187187- return &TrueQuery{}
188188- }
189189- return &FalseQuery{}
190190-}
191191-192192-func isConst(q Query) (bool, bool) {
193193- if _, ok := q.(*TrueQuery); ok {
194194- return true, true
195195- }
196196- if _, ok := q.(*FalseQuery); ok {
197197- return false, true
198198- }
199199- return false, false
200200-}
201201-202184func invertConst(q Query) Query {
203203- c, ok := isConst(q)
185185+ c, ok := q.(*Const)
204186 if ok {
205205- return constQuery(!c)
187187+ return &Const{!c.Value}
206188 }
207189 return q
208190}
209191210192func evalAndOrConstants(q Query, children []Query) Query {
211211- _, isAnd := q.(*AndQuery)
193193+ _, isAnd := q.(*And)
212194213195 children = mapQueryList(children, evalConstants)
214196215197 newCH := children[:0]
216198 for _, ch := range children {
217217- c, ok := isConst(ch)
199199+ c, ok := ch.(*Const)
218200 if ok {
219219- if c == isAnd {
201201+ if c.Value == isAnd {
220202 continue
221203 } else {
222204 return ch
···225207 newCH = append(newCH, ch)
226208 }
227209 if len(newCH) == 0 {
228228- return constQuery(isAnd)
210210+ return &Const{isAnd}
229211 }
230212 if isAnd {
231231- return &AndQuery{newCH}
213213+ return &And{newCH}
232214 }
233233- return &OrQuery{newCH}
215215+ return &Or{newCH}
234216}
235217236218func evalConstants(q Query) Query {
237219 switch s := q.(type) {
238238- case *AndQuery:
220220+ case *And:
239221 return evalAndOrConstants(q, s.Children)
240240- case *OrQuery:
222222+ case *Or:
241223 return evalAndOrConstants(q, s.Children)
242242- case *NotQuery:
224224+ case *Not:
243225 ch := evalConstants(s.Child)
244244- if _, ok := isConst(ch); ok {
226226+ if _, ok := ch.(*Const); ok {
245227 return invertConst(ch)
246228 }
247247- return &NotQuery{ch}
229229+ return &Not{ch}
248230 }
249231 return q
250232}
251233252252-func simplify(q Query) Query {
234234+func Simplify(q Query) Query {
253235 q = evalConstants(q)
254236 for {
255237 var changed bool
···262244 return q
263245}
264246265265-// mapQueryList runs f over the q.
266266-func mapQuery(q Query, f func(q Query) Query) Query {
247247+// Map runs f over the q.
248248+func Map(q Query, f func(q Query) Query) Query {
267249 switch s := q.(type) {
268268- case *AndQuery:
269269- return &AndQuery{Children: mapQueryList(s.Children, f)}
270270- case *OrQuery:
271271- return &OrQuery{Children: mapQueryList(s.Children, f)}
272272- case *NotQuery:
273273- return &NotQuery{Child: f(s.Child)}
250250+ case *And:
251251+ return &And{Children: mapQueryList(s.Children, f)}
252252+ case *Or:
253253+ return &Or{Children: mapQueryList(s.Children, f)}
254254+ case *Not:
255255+ return &Not{Child: f(s.Child)}
274256 }
275257 return f(q)
276258}
+26
query/bits.go
···11+// Copyright 2016 Google Inc. All rights reserved.
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package query
1616+1717+func toLower(in []byte) []byte {
1818+ out := make([]byte, len(in))
1919+ for i, c := range in {
2020+ if c >= 'A' && c <= 'Z' {
2121+ c = c - 'A' + 'a'
2222+ }
2323+ out[i] = c
2424+ }
2525+ return out
2626+}
+82
query/parse_test.go
···11+// Copyright 2016 Google Inc. All rights reserved.
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package query
1616+1717+import (
1818+ "reflect"
1919+ "regexp/syntax"
2020+ "testing"
2121+)
2222+2323+func mustParseRE(s string) *syntax.Regexp {
2424+ r, err := syntax.Parse(s, 0)
2525+ if err != nil {
2626+ panic(err)
2727+ }
2828+2929+ return r
3030+}
3131+3232+func TestParseQuery(t *testing.T) {
3333+ type testcase struct {
3434+ in string
3535+ out Query
3636+ hasErr bool
3737+ }
3838+3939+ for _, c := range []testcase{
4040+ {"sub-pixel", &Substring{Pattern: "sub-pixel"}, false},
4141+ {"abc", &Substring{Pattern: "abc"}, false},
4242+ {"\"abc bcd\"", &Substring{Pattern: "abc bcd"}, false},
4343+ {"abc bcd", &And{[]Query{
4444+ &Substring{Pattern: "abc"},
4545+ &Substring{Pattern: "bcd"},
4646+ }}, false},
4747+ {"-abc", &Not{&Substring{Pattern: "abc"}}, false},
4848+ {"regex:a.b", nil, true},
4949+5050+ {"abccase:yes", &Substring{Pattern: "abccase:yes"}, false},
5151+ {"file:abc", &Substring{Pattern: "abc", FileName: true}, false},
5252+ {"branch:pqr", &Branch{Name: "pqr"}, false},
5353+5454+ {"file:helpers.go byte", &And{[]Query{
5555+ &Substring{Pattern: "helpers.go", FileName: true},
5656+ &Substring{Pattern: "byte"},
5757+ }}, false},
5858+5959+ {"regex:abc[p-q]", &Regexp{mustParseRE("abc[p-q]")}, false},
6060+ {"repo:go", &Repo{"go"}, false},
6161+6262+ // case
6363+ {"abc case:yes", &Substring{Pattern: "abc", CaseSensitive: true}, false},
6464+ {"abc case:auto", &Substring{Pattern: "abc", CaseSensitive: false}, false},
6565+ {"ABC case:auto", &Substring{Pattern: "ABC", CaseSensitive: true}, false},
6666+ {"ABC case:\"auto\"", &Substring{Pattern: "ABC", CaseSensitive: true}, false},
6767+ // errors.
6868+ {"\"abc", nil, true},
6969+ {"\"a\\", nil, true},
7070+ {"case:foo", nil, true},
7171+ {"", nil, true},
7272+ } {
7373+ q, err := Parse(c.in)
7474+ if c.hasErr != (err != nil) {
7575+ t.Errorf("Parse(%s): error %v, value %v", c.in, err, q)
7676+ } else if q != nil {
7777+ if !reflect.DeepEqual(q, c.out) {
7878+ t.Errorf("Parse(%s): got %v want %v", c.in, q, c.out)
7979+ }
8080+ }
8181+ }
8282+}
+79
query/query_test.go
···11+// Copyright 2016 Google Inc. All rights reserved.
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package query
1616+1717+import (
1818+ "log"
1919+ "reflect"
2020+ "testing"
2121+)
2222+2323+var _ = log.Println
2424+2525+func TestQueryString(t *testing.T) {
2626+ q := &Or{[]Query{
2727+ &And{[]Query{
2828+ &Substring{Pattern: "hoi"},
2929+ &Not{&Substring{Pattern: "hai"}},
3030+ }}}}
3131+ got := q.String()
3232+ want := `(or (and substr:"hoi" (not substr:"hai")))`
3333+3434+ if got != want {
3535+ t.Errorf("got %s, want %s", got, want)
3636+ }
3737+}
3838+3939+func TestSimplify(t *testing.T) {
4040+ type testcase struct {
4141+ in Query
4242+ want Query
4343+ }
4444+4545+ cases := []testcase{
4646+ {
4747+ in: &Or{[]Query{
4848+ &Or{[]Query{
4949+ &And{[]Query{
5050+ &Substring{Pattern: "hoi"},
5151+ &Not{&Substring{Pattern: "hai"}},
5252+ }},
5353+ &Or{[]Query{
5454+ &Substring{Pattern: "zip"},
5555+ &Substring{Pattern: "zap"},
5656+ }},
5757+ }}}},
5858+ want: &Or{[]Query{
5959+ &And{[]Query{
6060+ &Substring{Pattern: "hoi"},
6161+ &Not{&Substring{Pattern: "hai"}},
6262+ }},
6363+ &Substring{Pattern: "zip"},
6464+ &Substring{Pattern: "zap"}},
6565+ }},
6666+ {in: &And{}, want: &Const{true}},
6767+ {in: &Or{}, want: &Const{false}},
6868+ {in: &And{[]Query{&Const{true}, &Const{false}}}, want: &Const{false}},
6969+ {in: &Or{[]Query{&Const{false}, &Const{true}}}, want: &Const{true}},
7070+ {in: &Not{&Const{true}}, want: &Const{false}},
7171+ }
7272+7373+ for _, c := range cases {
7474+ got := simplify(c.in)
7575+ if !reflect.DeepEqual(got, c.want) {
7676+ t.Errorf("got %s, want %s", got, c.want)
7777+ }
7878+ }
7979+}
+84
query/regexp.go
···11+// Copyright 2016 Google Inc. All rights reserved.
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package query
1616+1717+import (
1818+ "log"
1919+ "regexp/syntax"
2020+)
2121+2222+var _ = log.Println
2323+2424+func LowerRegexp(r *syntax.Regexp) *syntax.Regexp {
2525+ newRE := *r
2626+ switch r.Op {
2727+ case syntax.OpLiteral, syntax.OpCharClass:
2828+ for i, r := range newRE.Rune {
2929+ if r >= 'A' && r <= 'Z' {
3030+ newRE.Rune[i] = r + 'a' - 'A'
3131+ }
3232+ }
3333+ default:
3434+ for i, s := range newRE.Sub {
3535+ newRE.Sub[i] = LowerRegexp(s)
3636+ }
3737+ }
3838+3939+ return &newRE
4040+}
4141+4242+func RegexpToQuery(r *syntax.Regexp) Query {
4343+ q := regexpToQueryRecursive(r)
4444+ q = Simplify(q)
4545+ return q
4646+}
4747+4848+// regexpToQuery tries to distill a substring search query that
4949+// matches a superset of the regexp.
5050+func regexpToQueryRecursive(r *syntax.Regexp) Query {
5151+ // TODO - we could perhaps transform Begin/EndText in '\n'?
5252+ // TODO - we could perhaps transform CharClass in (OrQuery )
5353+ // if there are just a few runes, and part of a OpConcat?
5454+ switch r.Op {
5555+ case syntax.OpLiteral:
5656+ s := string(r.Rune)
5757+ if len(s) >= ngramSize {
5858+ return &Substring{Pattern: s}
5959+ }
6060+ case syntax.OpCapture:
6161+ return regexpToQueryRecursive(r.Sub[0])
6262+6363+ case syntax.OpPlus:
6464+ return regexpToQueryRecursive(r.Sub[0])
6565+6666+ case syntax.OpRepeat:
6767+ if r.Min >= 1 {
6868+ return regexpToQueryRecursive(r.Sub[0])
6969+ }
7070+7171+ case syntax.OpConcat, syntax.OpAlternate:
7272+ var qs []Query
7373+ for _, sr := range r.Sub {
7474+ if sq := regexpToQueryRecursive(sr); sq != nil {
7575+ qs = append(qs, sq)
7676+ }
7777+ }
7878+ if r.Op == syntax.OpConcat {
7979+ return &And{qs}
8080+ }
8181+ return &Or{qs}
8282+ }
8383+ return &Const{true}
8484+}
-79
query_test.go
···11-// Copyright 2016 Google Inc. All rights reserved.
22-//
33-// Licensed under the Apache License, Version 2.0 (the "License");
44-// you may not use this file except in compliance with the License.
55-// You may obtain a copy of the License at
66-//
77-// http://www.apache.org/licenses/LICENSE-2.0
88-//
99-// Unless required by applicable law or agreed to in writing, software
1010-// distributed under the License is distributed on an "AS IS" BASIS,
1111-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212-// See the License for the specific language governing permissions and
1313-// limitations under the License.
1414-1515-package zoekt
1616-1717-import (
1818- "log"
1919- "reflect"
2020- "testing"
2121-)
2222-2323-var _ = log.Println
2424-2525-func TestQueryString(t *testing.T) {
2626- q := &OrQuery{[]Query{
2727- &AndQuery{[]Query{
2828- &SubstringQuery{Pattern: "hoi"},
2929- &NotQuery{&SubstringQuery{Pattern: "hai"}},
3030- }}}}
3131- got := q.String()
3232- want := `(or (and substr:"hoi" (not substr:"hai")))`
3333-3434- if got != want {
3535- t.Errorf("got %s, want %s", got, want)
3636- }
3737-}
3838-3939-func TestSimplify(t *testing.T) {
4040- type testcase struct {
4141- in Query
4242- want Query
4343- }
4444-4545- cases := []testcase{
4646- {
4747- in: &OrQuery{[]Query{
4848- &OrQuery{[]Query{
4949- &AndQuery{[]Query{
5050- &SubstringQuery{Pattern: "hoi"},
5151- &NotQuery{&SubstringQuery{Pattern: "hai"}},
5252- }},
5353- &OrQuery{[]Query{
5454- &SubstringQuery{Pattern: "zip"},
5555- &SubstringQuery{Pattern: "zap"},
5656- }},
5757- }}}},
5858- want: &OrQuery{[]Query{
5959- &AndQuery{[]Query{
6060- &SubstringQuery{Pattern: "hoi"},
6161- &NotQuery{&SubstringQuery{Pattern: "hai"}},
6262- }},
6363- &SubstringQuery{Pattern: "zip"},
6464- &SubstringQuery{Pattern: "zap"}},
6565- }},
6666- {in: &AndQuery{}, want: &TrueQuery{}},
6767- {in: &OrQuery{}, want: &FalseQuery{}},
6868- {in: &AndQuery{[]Query{&TrueQuery{}, &FalseQuery{}}}, want: &FalseQuery{}},
6969- {in: &OrQuery{[]Query{&FalseQuery{}, &TrueQuery{}}}, want: &TrueQuery{}},
7070- {in: &NotQuery{&TrueQuery{}}, want: &FalseQuery{}},
7171- }
7272-7373- for _, c := range cases {
7474- got := simplify(c.in)
7575- if !reflect.DeepEqual(got, c.want) {
7676- t.Errorf("got %s, want %s", got, c.want)
7777- }
7878- }
7979-}
···11-package zoekt
22-33-import (
44- "log"
55- "regexp/syntax"
66-)
77-88-var _ = log.Println
99-1010-func lowerRegexp(r *syntax.Regexp) *syntax.Regexp {
1111- newRE := *r
1212- switch r.Op {
1313- case syntax.OpLiteral, syntax.OpCharClass:
1414- for i, r := range newRE.Rune {
1515- if r >= 'A' && r <= 'Z' {
1616- newRE.Rune[i] = r + 'a' - 'A'
1717- }
1818- }
1919- default:
2020- for i, s := range newRE.Sub {
2121- newRE.Sub[i] = lowerRegexp(s)
2222- }
2323- }
2424-2525- return &newRE
2626-}
2727-2828-func regexpToQuery(r *syntax.Regexp) Query {
2929- q := regexpToQueryRecursive(r)
3030- q = simplify(q)
3131- return q
3232-}
3333-3434-// regexpToQuery tries to distill a substring search query that
3535-// matches a superset of the regexp.
3636-func regexpToQueryRecursive(r *syntax.Regexp) Query {
3737- // TODO - we could perhaps transform Begin/EndText in '\n'?
3838- // TODO - we could perhaps transform CharClass in (OrQuery )
3939- // if there are just a few runes, and part of a OpConcat?
4040- switch r.Op {
4141- case syntax.OpLiteral:
4242- s := string(r.Rune)
4343- if len(s) >= ngramSize {
4444- return &SubstringQuery{Pattern: s}
4545- }
4646- case syntax.OpCapture:
4747- return regexpToQuery(r.Sub[0])
4848-4949- case syntax.OpPlus:
5050- return regexpToQuery(r.Sub[0])
5151-5252- case syntax.OpRepeat:
5353- if r.Min >= 1 {
5454- return regexpToQuery(r.Sub[0])
5555- }
5656-5757- case syntax.OpConcat, syntax.OpAlternate:
5858- var qs []Query
5959- for _, sr := range r.Sub {
6060- if sq := regexpToQuery(sr); sq != nil {
6161- qs = append(qs, sq)
6262- }
6363- }
6464- if r.Op == syntax.OpConcat {
6565- return &AndQuery{qs}
6666- }
6767- return &OrQuery{qs}
6868- }
6969- return &TrueQuery{}
7070-}
+28-14
regexp_test.go
query/regexp_test.go
···11-package zoekt
11+// Copyright 2016 Google Inc. All rights reserved.
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package query
216317import (
418 "log"
···4458 }
45594660 cases := []testcase{
4747- {"(foo|)bar", &SubstringQuery{Pattern: "bar"}},
4848- {"(foo|)", &TrueQuery{}},
4949- {"(foo|bar)baz.*bla", &AndQuery{[]Query{
5050- &OrQuery{[]Query{
5151- &SubstringQuery{Pattern: "foo"},
5252- &SubstringQuery{Pattern: "bar"},
6161+ {"(foo|)bar", &Substring{Pattern: "bar"}},
6262+ {"(foo|)", &Const{true}},
6363+ {"(foo|bar)baz.*bla", &And{[]Query{
6464+ &Or{[]Query{
6565+ &Substring{Pattern: "foo"},
6666+ &Substring{Pattern: "bar"},
5367 }},
5454- &SubstringQuery{Pattern: "baz"},
5555- &SubstringQuery{Pattern: "bla"},
6868+ &Substring{Pattern: "baz"},
6969+ &Substring{Pattern: "bla"},
5670 }}},
5771 {"^[a-z](People)+barrabas$",
5858- &AndQuery{[]Query{
5959- &SubstringQuery{Pattern: "People"},
6060- &SubstringQuery{Pattern: "barrabas"},
7272+ &And{[]Query{
7373+ &Substring{Pattern: "People"},
7474+ &Substring{Pattern: "barrabas"},
6175 }}},
6276 }
6377···6882 continue
6983 }
70847171- got := regexpToQuery(r)
8585+ got := RegexpToQuery(r)
7286 if !reflect.DeepEqual(c.want, got) {
7387 t.Errorf("regexpToQuery(%q): got %v, want %v", c.in, got, c.want)
7488 }
···7892func TestLowerRegexp(t *testing.T) {
7993 in := "[a-zA-Z]fooBAR"
8094 re := mustParseRE(in)
8181- got := lowerRegexp(re)
9595+ got := LowerRegexp(re)
8296 want := "[a-za-z]foobar"
8397 if got.String() != want {
8498 t.Errorf("got %s, want %s", got, want)