From 1ae5cbc851ca55214a59e675240cd2dfd1efb276 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 24 Jul 2020 10:49:20 -0700 Subject: [PATCH] query: Handle #not-match? in rust, wasm bindings --- cli/src/tests/query_test.rs | 9 ++++++++- lib/binding_rust/lib.rs | 10 ++++++---- lib/binding_web/binding.js | 4 +++- lib/binding_web/test/query-test.js | 9 ++++++++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index c304f3b4..914d41cd 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -1470,12 +1470,17 @@ fn test_query_captures_with_text_conditions() { ((identifier) @function.builtin (#eq? @function.builtin "require")) - (identifier) @variable + ((identifier) @variable + (#not-match? @variable "^(lambda|load)$")) "#, ) .unwrap(); let source = " + toad + load + panda + lambda const ab = require('./ab'); new Cd(EF); "; @@ -1489,6 +1494,8 @@ fn test_query_captures_with_text_conditions() { assert_eq!( collect_captures(captures, &query, source), &[ + ("variable", "toad"), + ("variable", "panda"), ("variable", "ab"), ("function.builtin", "require"), ("variable", "require"), diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index c0aba32f..ec7cd791 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -169,7 +169,7 @@ pub enum QueryError { enum TextPredicate { CaptureEqString(u32, String, bool), CaptureEqCapture(u32, u32, bool), - CaptureMatchString(u32, regex::bytes::Regex), + CaptureMatchString(u32, regex::bytes::Regex, bool), } impl Language { @@ -1298,7 +1298,7 @@ impl Query { }); } - "match?" => { + "match?" | "not-match?" => { if p.len() != 3 { return Err(QueryError::Predicate(format!( "Wrong number of arguments to #match? predicate. Expected 2, got {}.", @@ -1318,12 +1318,14 @@ impl Query { ))); } + let is_positive = operator_name == "match?"; let regex = &string_values[p[2].value_id as usize]; text_predicates.push(TextPredicate::CaptureMatchString( p[1].value_id, regex::bytes::Regex::new(regex).map_err(|_| { QueryError::Predicate(format!("Invalid regex '{}'", regex)) })?, + is_positive, )); } @@ -1607,9 +1609,9 @@ impl<'a> QueryMatch<'a> { let node = self.capture_for_index(*i).unwrap(); (text_callback(node).as_ref() == s.as_bytes()) == *is_positive } - TextPredicate::CaptureMatchString(i, r) => { + TextPredicate::CaptureMatchString(i, r, is_positive) => { let node = self.capture_for_index(*i).unwrap(); - r.is_match(text_callback(node).as_ref()) + r.is_match(text_callback(node).as_ref()) == *is_positive } }) } diff --git a/lib/binding_web/binding.js b/lib/binding_web/binding.js index 567b7eb3..3a193ef9 100644 --- a/lib/binding_web/binding.js +++ b/lib/binding_web/binding.js @@ -784,6 +784,8 @@ class Language { } break; + case 'not-match?': + isPositive = false; case 'match?': if (steps.length !== 3) throw new Error( `Wrong number of arguments to \`#match?\` predicate. Expected 2, got ${steps.length - 1}.` @@ -798,7 +800,7 @@ class Language { const regex = new RegExp(steps[2].value); textPredicates[i].push(function(captures) { for (const c of captures) { - if (c.name === captureName) return regex.test(c.node.text); + if (c.name === captureName) return regex.test(c.node.text) === isPositive; } return false; }); diff --git a/lib/binding_web/test/query-test.js b/lib/binding_web/test/query-test.js index 9dda9834..9d1e24e1 100644 --- a/lib/binding_web/test/query-test.js +++ b/lib/binding_web/test/query-test.js @@ -126,12 +126,17 @@ describe("Query", () => { it("handles conditions that compare the text of capture to literal strings", () => { tree = parser.parse(` + lambda + panda + load + toad const ab = require('./ab'); new Cd(EF); `); query = JavaScript.query(` - (identifier) @variable + ((identifier) @variable + (#not-match? @variable "^(lambda|load)$")) ((identifier) @function.builtin (#eq? @function.builtin "require")) @@ -145,6 +150,8 @@ describe("Query", () => { const captures = query.captures(tree.rootNode); assert.deepEqual(formatCaptures(captures), [ + { name: "variable", text: "panda" }, + { name: "variable", text: "toad" }, { name: "variable", text: "ab" }, { name: "variable", text: "require" }, { name: "function.builtin", text: "require" },