diff options
Diffstat (limited to 'internal/cond')
| -rw-r--r-- | internal/cond/compile.go | 1 | ||||
| -rw-r--r-- | internal/cond/compile_test.go | 21 | ||||
| -rw-r--r-- | internal/cond/eval.go | 17 | ||||
| -rw-r--r-- | internal/cond/eval_test.go | 12 | ||||
| -rw-r--r-- | internal/cond/types.go | 24 |
5 files changed, 66 insertions, 9 deletions
diff --git a/internal/cond/compile.go b/internal/cond/compile.go index 590d61a..94118ac 100644 --- a/internal/cond/compile.go +++ b/internal/cond/compile.go @@ -266,6 +266,7 @@ func (c *compiler) compileContent(n *sexp.Node) *node { continue } keywords = append(keywords, keyword{norm: normed, src: a.Text}) + c.cond.Keywords = append(c.cond.Keywords, Keyword{Opt: c.opt, Norm: normed}) } if bad { return nil diff --git a/internal/cond/compile_test.go b/internal/cond/compile_test.go index 2a07ad6..8432365 100644 --- a/internal/cond/compile_test.go +++ b/internal/cond/compile_test.go @@ -165,3 +165,24 @@ func TestGroupsMatchSpec(t *testing.T) { } } } + +// TestCompileCollectsKeywords: every content keyword is recorded as +// compared, normalised under the compile options, and its key names those +// options. +func TestCompileCollectsKeywords(t *testing.T) { + opt := Options{IgnoreCase: true, Fold: true} + c, errs := Compile("d.conf", nodes(t, `(or (content "Spółka" "x") (not (content "NIP")))`), opt) + if len(errs) > 0 { + t.Fatal(errs) + } + want := []Keyword{{Opt: opt, Norm: "spolka"}, {Opt: opt, Norm: "x"}, {Opt: opt, Norm: "nip"}} + if !reflect.DeepEqual(c.Keywords, want) { + t.Errorf("Keywords = %+v, want %+v", c.Keywords, want) + } + if got := want[0].Key(); got != "if:spolka" { + t.Errorf("Key = %q", got) + } + if got := KeywordKey(Options{}, "NIP"); got != "sn:NIP" { + t.Errorf("strict, unfolded key = %q", got) + } +} diff --git a/internal/cond/eval.go b/internal/cond/eval.go index e3093af..3cec110 100644 --- a/internal/cond/eval.go +++ b/internal/cond/eval.go @@ -19,7 +19,10 @@ type Facts interface { Size() int64 ModTime() time.Time Now() time.Time - Content(ignoreCase, fold bool) (string, error) // normalised with norm.Text + // ContentContains reports the index of the first of keywords, each + // normalised under opt with norm.Text, that the file's text contains, or + // -1 when it contains none. + ContentContains(opt Options, keywords []string) (int, error) Duplicate(dirs []string) (original string, ok bool, err error) Matched() bool // an earlier rule matched this file } @@ -167,14 +170,16 @@ func (c *Cond) evalLeaf(n *node, f Facts) (ok bool, reason, warn string, caps [] return false, "", "", nil case kContent: - text, err := f.Content(c.opt.IgnoreCase, c.opt.Fold) + norms := make([]string, len(n.keywords)) + for i, kw := range n.keywords { + norms[i] = kw.norm + } + i, err := f.ContentContains(c.opt, norms) if err != nil { return false, "", "content unreadable: " + err.Error(), nil } - for _, kw := range n.keywords { - if strings.Contains(text, kw.norm) { - return true, `content "` + kw.src + `"`, "", nil - } + if i >= 0 { + return true, `content "` + n.keywords[i].src + `"`, "", nil } return false, "", "", nil diff --git a/internal/cond/eval_test.go b/internal/cond/eval_test.go index 3607978..8891de7 100644 --- a/internal/cond/eval_test.go +++ b/internal/cond/eval_test.go @@ -37,12 +37,18 @@ func (f *fake) Size() int64 { return f.size } func (f *fake) ModTime() time.Time { return now.Add(-f.age) } func (f *fake) Now() time.Time { return now } func (f *fake) Matched() bool { return f.matched } -func (f *fake) Content(ic, fold bool) (string, error) { +func (f *fake) ContentContains(opt Options, keywords []string) (int, error) { f.contentCalls++ if f.rawErr != nil { - return "", f.rawErr + return -1, f.rawErr } - return norm.Text(f.raw, ic, fold), nil + text := norm.Text(f.raw, opt.IgnoreCase, opt.Fold) + for i, kw := range keywords { + if strings.Contains(text, kw) { + return i, nil + } + } + return -1, nil } func (f *fake) Duplicate(dirs []string) (string, bool, error) { return f.dupOrig, f.dupOK, nil } diff --git a/internal/cond/types.go b/internal/cond/types.go index 9455f63..28a4fc9 100644 --- a/internal/cond/types.go +++ b/internal/cond/types.go @@ -24,6 +24,30 @@ type Cond struct { opt Options // the case/fold settings conditions were compiled with; Task 8 needs them again at eval time UsesContent bool // some content test exists DupDirs [][]string // the raw directory arguments of each duplicate test, in order + Keywords []Keyword // every content keyword, as compiled, in order +} + +// Keyword is one content keyword as a content test compares it: normalised +// under the options it was compiled with. +type Keyword struct { + Opt Options + Norm string +} + +// Key is the keyword's identity across runs, for the keyword cache: its +// options and its normalised text. +func (k Keyword) Key() string { return KeywordKey(k.Opt, k.Norm) } + +// KeywordKey is Keyword.Key for opt and an already normalised keyword. +func KeywordKey(opt Options, norm string) string { + b := []byte("sn:") + if opt.IgnoreCase { + b[0] = 'i' + } + if opt.Fold { + b[1] = 'f' + } + return string(b) + norm } // kind is what a compiled node tests, or how it combines its children. |
