diff --git a/all_test.go b/all_test.go index dd8ae18..948e8ec 100644 --- a/all_test.go +++ b/all_test.go @@ -240,6 +240,10 @@ var libraryTable = []struct { {"//*[contains(born,'1922')]/name", "Charles M Schulz"}, {"library/book[not(@id)]", exists(false)}, {"library/book[not(@foo) and @id='b0883556316']/isbn", []string{"0883556316"}}, + {"library/book[not(@id='b0883556316')]/isbn", []string{"0836217462"}}, + {"library/book/character[not(@id='Snoopy' and ./born='1950-10-04')]", exists(true)}, + {"library/book/character[@id='Snoopy' and ./born='1950-10-04' or not(@id='Lucy') and ./born='1952-03-03']/born", []string{"1950-10-04"}}, + {"library/book[@id='b0836217462']/character[not(@id='Snoopy' and ./born='1950-10-04' or @id='Lucy' and ./born='1952-03-03')]/born", []string{"1966-08-22", "1951-05-30"}}, // Multiple predicates. {"library/book/character[@id='Snoopy' and ./born='1950-10-04']/born", []string{"1950-10-04"}}, diff --git a/doc.go b/doc.go index e81d737..7c7a06e 100644 --- a/doc.go +++ b/doc.go @@ -15,7 +15,7 @@ // - All axes are supported ("child", "following-sibling", etc) // - All abbreviated forms are supported (".", "//", etc) // - All node types except for namespace are supported -// - Predicates may be [N], [path], [not(path)], [path=literal] or [contains(path, literal)] +// - Predicates may be [N], [path], [not(predicate)], [path=literal] or [contains(path, literal)] // - Predicates may be joined with "or", "and", and parenthesis // - Richer expressions and namespaces are not supported // diff --git a/path.go b/path.go index db38ed5..df00f6e 100644 --- a/path.go +++ b/path.go @@ -156,10 +156,7 @@ func (s *pathStepState) test(pred predicate) bool { } } case notPredicate: - iter := pred.path.Iter(s.node) - if !iter.Next() { - return true - } + return !s.test(pred.uniSub) case andPredicate: for _, sub := range pred.sub { if !s.test(sub) { @@ -382,7 +379,7 @@ type containsPredicate struct { } type notPredicate struct { - path *Path + uniSub predicate } type andPredicate struct { @@ -565,13 +562,14 @@ func (c *pathCompiler) parsePath() (path *Path, err error) { type state struct { sub []predicate and bool + not bool } var stack []state var sub []predicate var and bool NextPred: if c.skipByte('(') { - stack = append(stack, state{sub, and}) + stack = append(stack, state{sub: sub, and: and}) sub = nil and = false } @@ -601,16 +599,10 @@ func (c *pathCompiler) parsePath() (path *Path, err error) { } next = containsPredicate{path, value} } else if c.skipString("not(") { - // TODO Generalize to handle any predicate expression. - path, err := c.parsePath() - if err != nil { - return nil, err - } - c.skipSpaces() - if !c.skipByte(')') { - return nil, c.errorf("not() missing ')'") - } - next = notPredicate{path} + stack = append(stack, state{sub: sub, and: and, not: true}) + sub = nil + and = false + goto NextPred } else { path, err := c.parsePath() if err != nil { @@ -669,6 +661,9 @@ func (c *pathCompiler) parsePath() (path *Path, err error) { stack = stack[:len(stack)-1] sub = s.sub and = s.and + if s.not { + next = notPredicate{next} + } goto HandleNext } if len(stack) > 0 {