Handle is?, is-not?, and set! predicate functions in queries
This commit is contained in:
parent
b15e90bd26
commit
27149902f8
4 changed files with 256 additions and 142 deletions
|
|
@ -1,7 +1,9 @@
|
|||
use super::helpers::allocations;
|
||||
use super::helpers::fixtures::get_language;
|
||||
use std::fmt::Write;
|
||||
use tree_sitter::{Node, Parser, Query, QueryCapture, QueryCursor, QueryError, QueryMatch};
|
||||
use tree_sitter::{
|
||||
Node, Parser, Query, QueryCapture, QueryCursor, QueryError, QueryMatch, QueryProperty,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_query_errors_on_invalid_syntax() {
|
||||
|
|
@ -642,20 +644,30 @@ fn test_query_captures_with_set_properties() {
|
|||
r#"
|
||||
((call_expression (identifier) @foo)
|
||||
(set! name something)
|
||||
(set! age 24))
|
||||
(set! cool))
|
||||
|
||||
(property_identifier) @bar"#,
|
||||
((property_identifier) @bar
|
||||
(is? cool)
|
||||
(is-not? name something))"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
query.pattern_properties(0),
|
||||
query.property_settings(0),
|
||||
&[
|
||||
("name".to_string(), "something".to_string()),
|
||||
("age".to_string(), "24".to_string()),
|
||||
QueryProperty::new("name", Some("something"), None),
|
||||
QueryProperty::new("cool", None, None),
|
||||
]
|
||||
);
|
||||
assert_eq!(query.property_settings(1), &[]);
|
||||
assert_eq!(query.property_predicates(0), &[]);
|
||||
assert_eq!(
|
||||
query.property_predicates(1),
|
||||
&[
|
||||
(QueryProperty::new("cool", None, None), true),
|
||||
(QueryProperty::new("name", Some("something"), None), false),
|
||||
]
|
||||
);
|
||||
assert_eq!(query.pattern_properties(1), &[])
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,18 +139,26 @@ pub struct TreePropertyCursor<'a, P> {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum QueryPredicate {
|
||||
enum TextPredicate {
|
||||
CaptureEqString(u32, String),
|
||||
CaptureEqCapture(u32, u32),
|
||||
CaptureMatchString(u32, regex::bytes::Regex),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct QueryProperty {
|
||||
pub key: Box<str>,
|
||||
pub value: Option<Box<str>>,
|
||||
pub capture_id: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Query {
|
||||
ptr: NonNull<ffi::TSQuery>,
|
||||
capture_names: Vec<String>,
|
||||
predicates: Vec<Vec<QueryPredicate>>,
|
||||
properties: Vec<Box<[(String, String)]>>,
|
||||
text_predicates: Vec<Box<[TextPredicate]>>,
|
||||
property_settings: Vec<Box<[QueryProperty]>>,
|
||||
property_predicates: Vec<Box<[(QueryProperty, bool)]>>,
|
||||
}
|
||||
|
||||
pub struct QueryCursor(NonNull<ffi::TSQueryCursor>);
|
||||
|
|
@ -1002,8 +1010,9 @@ impl Query {
|
|||
let mut result = Query {
|
||||
ptr: unsafe { NonNull::new_unchecked(ptr) },
|
||||
capture_names: Vec::with_capacity(capture_count as usize),
|
||||
predicates: Vec::with_capacity(pattern_count),
|
||||
properties: Vec::with_capacity(pattern_count),
|
||||
text_predicates: Vec::with_capacity(pattern_count),
|
||||
property_predicates: Vec::with_capacity(pattern_count),
|
||||
property_settings: Vec::with_capacity(pattern_count),
|
||||
};
|
||||
|
||||
// Build a vector of strings to store the capture names.
|
||||
|
|
@ -1044,8 +1053,9 @@ impl Query {
|
|||
let type_capture = ffi::TSQueryPredicateStepType_TSQueryPredicateStepTypeCapture;
|
||||
let type_string = ffi::TSQueryPredicateStepType_TSQueryPredicateStepTypeString;
|
||||
|
||||
let mut pattern_properties = Vec::new();
|
||||
let mut pattern_predicates = Vec::new();
|
||||
let mut text_predicates = Vec::new();
|
||||
let mut property_predicates = Vec::new();
|
||||
let mut property_settings = Vec::new();
|
||||
for p in predicate_steps.split(|s| s.type_ == type_done) {
|
||||
if p.is_empty() {
|
||||
continue;
|
||||
|
|
@ -1075,10 +1085,10 @@ impl Query {
|
|||
)));
|
||||
}
|
||||
|
||||
pattern_predicates.push(if p[2].type_ == type_capture {
|
||||
QueryPredicate::CaptureEqCapture(p[1].value_id, p[2].value_id)
|
||||
text_predicates.push(if p[2].type_ == type_capture {
|
||||
TextPredicate::CaptureEqCapture(p[1].value_id, p[2].value_id)
|
||||
} else {
|
||||
QueryPredicate::CaptureEqString(
|
||||
TextPredicate::CaptureEqString(
|
||||
p[1].value_id,
|
||||
string_values[p[2].value_id as usize].clone(),
|
||||
)
|
||||
|
|
@ -1106,7 +1116,7 @@ impl Query {
|
|||
}
|
||||
|
||||
let regex = &string_values[p[2].value_id as usize];
|
||||
pattern_predicates.push(QueryPredicate::CaptureMatchString(
|
||||
text_predicates.push(TextPredicate::CaptureMatchString(
|
||||
p[1].value_id,
|
||||
regex::bytes::Regex::new(regex).map_err(|_| {
|
||||
QueryError::Predicate(format!("Invalid regex '{}'", regex))
|
||||
|
|
@ -1114,23 +1124,22 @@ impl Query {
|
|||
));
|
||||
}
|
||||
|
||||
"set!" => {
|
||||
if p.len() != 3 {
|
||||
return Err(QueryError::Predicate(format!(
|
||||
"Wrong number of arguments to set! predicate. Expected 2, got {}.",
|
||||
p.len() - 1
|
||||
)));
|
||||
}
|
||||
if p[1].type_ != type_string || p[2].type_ != type_string {
|
||||
return Err(QueryError::Predicate(
|
||||
"Argument to set! predicate must be strings.".to_string(),
|
||||
));
|
||||
}
|
||||
let key = &string_values[p[1].value_id as usize];
|
||||
let value = &string_values[p[2].value_id as usize];
|
||||
"set!" => property_settings.push(Self::parse_property(
|
||||
"set!",
|
||||
&result.capture_names,
|
||||
&string_values,
|
||||
&p[1..],
|
||||
)?),
|
||||
|
||||
pattern_properties.push((key.to_string(), value.to_string()));
|
||||
}
|
||||
"is?" | "is-not?" => property_predicates.push((
|
||||
Self::parse_property(
|
||||
&operator_name,
|
||||
&result.capture_names,
|
||||
&string_values,
|
||||
&p[1..],
|
||||
)?,
|
||||
operator_name == "is?",
|
||||
)),
|
||||
|
||||
_ => {
|
||||
return Err(QueryError::Predicate(format!(
|
||||
|
|
@ -1142,20 +1151,24 @@ impl Query {
|
|||
}
|
||||
|
||||
result
|
||||
.properties
|
||||
.push(pattern_properties.into_boxed_slice());
|
||||
result.predicates.push(pattern_predicates);
|
||||
.text_predicates
|
||||
.push(text_predicates.into_boxed_slice());
|
||||
result
|
||||
.property_predicates
|
||||
.push(property_predicates.into_boxed_slice());
|
||||
result
|
||||
.property_settings
|
||||
.push(property_settings.into_boxed_slice());
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn start_byte_for_pattern(&self, pattern_index: usize) -> usize {
|
||||
if pattern_index >= self.predicates.len() {
|
||||
if pattern_index >= self.text_predicates.len() {
|
||||
panic!(
|
||||
"Pattern index is {} but the pattern count is {}",
|
||||
pattern_index,
|
||||
self.predicates.len(),
|
||||
self.text_predicates.len(),
|
||||
);
|
||||
}
|
||||
unsafe {
|
||||
|
|
@ -1171,8 +1184,73 @@ impl Query {
|
|||
&self.capture_names
|
||||
}
|
||||
|
||||
pub fn pattern_properties(&self, index: usize) -> &[(String, String)] {
|
||||
&self.properties[index]
|
||||
pub fn property_predicates(&self, index: usize) -> &[(QueryProperty, bool)] {
|
||||
&self.property_predicates[index]
|
||||
}
|
||||
|
||||
pub fn property_settings(&self, index: usize) -> &[QueryProperty] {
|
||||
&self.property_settings[index]
|
||||
}
|
||||
|
||||
fn parse_property(
|
||||
function_name: &str,
|
||||
capture_names: &[String],
|
||||
string_values: &[String],
|
||||
args: &[ffi::TSQueryPredicateStep],
|
||||
) -> Result<QueryProperty, QueryError> {
|
||||
if args.len() == 0 || args.len() > 3 {
|
||||
return Err(QueryError::Predicate(format!(
|
||||
"Wrong number of arguments to {} predicate. Expected 1 to 3, got {}.",
|
||||
function_name,
|
||||
args.len(),
|
||||
)));
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
let mut capture_id = None;
|
||||
if args[i].type_ == ffi::TSQueryPredicateStepType_TSQueryPredicateStepTypeCapture {
|
||||
capture_id = Some(args[i].value_id as usize);
|
||||
i += 1;
|
||||
|
||||
if i == args.len() {
|
||||
return Err(QueryError::Predicate(format!(
|
||||
"No key specified for {} predicate.",
|
||||
function_name,
|
||||
)));
|
||||
}
|
||||
if args[i].type_ == ffi::TSQueryPredicateStepType_TSQueryPredicateStepTypeCapture {
|
||||
return Err(QueryError::Predicate(format!(
|
||||
"Invalid arguments to {} predicate. Expected string, got @{}",
|
||||
function_name, capture_names[args[i].value_id as usize]
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let key = &string_values[args[i].value_id as usize];
|
||||
i += 1;
|
||||
|
||||
let mut value = None;
|
||||
if i < args.len() {
|
||||
if args[i].type_ == ffi::TSQueryPredicateStepType_TSQueryPredicateStepTypeCapture {
|
||||
return Err(QueryError::Predicate(format!(
|
||||
"Invalid arguments to {} predicate. Expected string, got @{}",
|
||||
function_name, capture_names[args[i].value_id as usize]
|
||||
)));
|
||||
}
|
||||
value = Some(string_values[args[i].value_id as usize].as_str());
|
||||
}
|
||||
|
||||
Ok(QueryProperty::new(key, value, capture_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryProperty {
|
||||
pub fn new(key: &str, value: Option<&str>, capture_id: Option<usize>) -> Self {
|
||||
QueryProperty {
|
||||
capture_id,
|
||||
key: key.to_string().into_boxed_str(),
|
||||
value: value.map(|s| s.to_string().into_boxed_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1196,7 +1274,7 @@ impl QueryCursor {
|
|||
if ffi::ts_query_cursor_next_match(ptr, m.as_mut_ptr()) {
|
||||
let m = m.assume_init();
|
||||
let captures = slice::from_raw_parts(m.captures, m.capture_count as usize);
|
||||
if Self::captures_match_condition(
|
||||
if Self::captures_match_text_predicates(
|
||||
query,
|
||||
captures,
|
||||
m.pattern_index as usize,
|
||||
|
|
@ -1234,7 +1312,7 @@ impl QueryCursor {
|
|||
) {
|
||||
let m = m.assume_init();
|
||||
let captures = slice::from_raw_parts(m.captures, m.capture_count as usize);
|
||||
if Self::captures_match_condition(
|
||||
if Self::captures_match_text_predicates(
|
||||
query,
|
||||
captures,
|
||||
m.pattern_index as usize,
|
||||
|
|
@ -1256,25 +1334,25 @@ impl QueryCursor {
|
|||
})
|
||||
}
|
||||
|
||||
fn captures_match_condition<'a>(
|
||||
fn captures_match_text_predicates<'a>(
|
||||
query: &'a Query,
|
||||
captures: &'a [ffi::TSQueryCapture],
|
||||
pattern_index: usize,
|
||||
text_callback: &mut impl FnMut(Node<'a>) -> &'a [u8],
|
||||
) -> bool {
|
||||
query.predicates[pattern_index]
|
||||
query.text_predicates[pattern_index]
|
||||
.iter()
|
||||
.all(|predicate| match predicate {
|
||||
QueryPredicate::CaptureEqCapture(i, j) => {
|
||||
TextPredicate::CaptureEqCapture(i, j) => {
|
||||
let node1 = Self::capture_for_id(captures, *i).unwrap();
|
||||
let node2 = Self::capture_for_id(captures, *j).unwrap();
|
||||
text_callback(node1) == text_callback(node2)
|
||||
}
|
||||
QueryPredicate::CaptureEqString(i, s) => {
|
||||
TextPredicate::CaptureEqString(i, s) => {
|
||||
let node = Self::capture_for_id(captures, *i).unwrap();
|
||||
text_callback(node) == s.as_bytes()
|
||||
}
|
||||
QueryPredicate::CaptureMatchString(i, r) => {
|
||||
TextPredicate::CaptureMatchString(i, r) => {
|
||||
let node = Self::capture_for_id(captures, *i).unwrap();
|
||||
r.is_match(text_callback(node))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -719,7 +719,9 @@ class Language {
|
|||
stringValues[i] = UTF8ToString(valueAddress, nameLength);
|
||||
}
|
||||
|
||||
const properties = new Array(patternCount);
|
||||
const setProperties = new Array(patternCount);
|
||||
const assertedProperties = new Array(patternCount);
|
||||
const refutedProperties = new Array(patternCount);
|
||||
const predicates = new Array(patternCount);
|
||||
for (let i = 0; i < patternCount; i++) {
|
||||
const predicatesAddress = C._ts_query_predicates_for_pattern(
|
||||
|
|
@ -730,7 +732,6 @@ class Language {
|
|||
const stepCount = getValue(TRANSFER_BUFFER, 'i32');
|
||||
|
||||
predicates[i] = [];
|
||||
properties[i] = null;
|
||||
|
||||
const steps = [];
|
||||
let stepAddress = predicatesAddress;
|
||||
|
|
@ -744,18 +745,96 @@ class Language {
|
|||
} else if (stepType === PREDICATE_STEP_TYPE_STRING) {
|
||||
steps.push({type: 'string', value: stringValues[stepValueId]});
|
||||
} else if (steps.length > 0) {
|
||||
const predicate = buildQueryPredicate(steps);
|
||||
if (typeof predicate === 'function') {
|
||||
predicates[i].push(predicate);
|
||||
} else {
|
||||
if (!properties[i]) properties[i] = {};
|
||||
properties[i][predicate.key] = predicate.value;
|
||||
if (steps[0].type !== 'string') {
|
||||
throw new Error('Predicates must begin with a literal value');
|
||||
}
|
||||
const operator = steps[0].value;
|
||||
switch (operator) {
|
||||
case 'eq?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`eq?\` predicate. Expected 2, got ${steps.length - 1}`
|
||||
);
|
||||
if (steps[1].type !== 'capture') throw new Error(
|
||||
`First argument of \`eq?\` predicate must be a capture. Got "${steps[1].value}"`
|
||||
);
|
||||
if (steps[2].type === 'capture') {
|
||||
const captureName1 = steps[1].name;
|
||||
const captureName2 = steps[2].name;
|
||||
predicates[i].push(function(captures) {
|
||||
let node1, node2
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName1) node1 = c.node;
|
||||
if (c.name === captureName2) node2 = c.node;
|
||||
}
|
||||
return node1.text === node2.text
|
||||
});
|
||||
} else {
|
||||
const captureName = steps[1].name;
|
||||
const stringValue = steps[2].value;
|
||||
predicates[i].push(function(captures) {
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName) return c.node.text === stringValue;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'match?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`match?\` predicate. Expected 2, got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps[1].type !== 'capture') throw new Error(
|
||||
`First argument of \`match?\` predicate must be a capture. Got "${steps[1].value}".`
|
||||
);
|
||||
if (steps[2].type !== 'string') throw new Error(
|
||||
`Second argument of \`match?\` predicate must be a string. Got @${steps[2].value}.`
|
||||
);
|
||||
const captureName = steps[1].name;
|
||||
const regex = new RegExp(steps[2].value);
|
||||
predicates[i].push(function(captures) {
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName) return regex.test(c.node.text);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
break;
|
||||
|
||||
case 'set!':
|
||||
if (steps.length < 2 || steps.length > 3) throw new Error(
|
||||
`Wrong number of arguments to \`set!\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps.some(s => s.type !== 'string')) throw new Error(
|
||||
`Arguments to \`set!\` predicate must be a strings.".`
|
||||
);
|
||||
if (!setProperties[i]) setProperties[i] = {};
|
||||
setProperties[i][steps[1].value] = steps[2] ? steps[2].value : null;
|
||||
break;
|
||||
|
||||
case 'is?':
|
||||
case 'is-not?':
|
||||
if (steps.length < 2 || steps.length > 3) throw new Error(
|
||||
`Wrong number of arguments to \`${operator}\` predicate. Expected 1 or 2. Got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps.some(s => s.type !== 'string')) throw new Error(
|
||||
`Arguments to \`${operator}\` predicate must be a strings.".`
|
||||
);
|
||||
const properties = operator === 'is?' ? assertedProperties : refutedProperties;
|
||||
if (!properties[i]) properties[i] = {};
|
||||
properties[i][steps[1].value] = steps[2] ? steps[2].value : null;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown query predicate \`${steps[0].value}\``);
|
||||
}
|
||||
|
||||
steps.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Object.freeze(properties[i]);
|
||||
Object.freeze(setProperties[i]);
|
||||
Object.freeze(assertedProperties[i]);
|
||||
Object.freeze(refutedProperties[i]);
|
||||
}
|
||||
|
||||
C._free(sourceAddress);
|
||||
|
|
@ -764,7 +843,9 @@ class Language {
|
|||
address,
|
||||
captureNames,
|
||||
predicates,
|
||||
Object.freeze(properties)
|
||||
Object.freeze(setProperties),
|
||||
Object.freeze(assertedProperties),
|
||||
Object.freeze(refutedProperties)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -801,12 +882,17 @@ class Language {
|
|||
}
|
||||
|
||||
class Query {
|
||||
constructor(internal, address, captureNames, predicates, properties) {
|
||||
constructor(
|
||||
internal, address, captureNames, predicates,
|
||||
setProperties, assertedProperties, refutedProperties
|
||||
) {
|
||||
assertInternal(internal);
|
||||
this[0] = address;
|
||||
this.captureNames = captureNames;
|
||||
this.predicates = predicates;
|
||||
this.patternProperties = properties;
|
||||
this.setProperties = setProperties;
|
||||
this.assertedProperties = assertedProperties;
|
||||
this.refutedProperties = refutedProperties;
|
||||
}
|
||||
|
||||
delete() {
|
||||
|
|
@ -843,10 +929,13 @@ class Query {
|
|||
address = unmarshalCaptures(this, node.tree, address, captures);
|
||||
if (this.predicates[pattern].every(p => p(captures))) {
|
||||
result[i] = {pattern, captures};
|
||||
const setProperties = this.setProperties[pattern];
|
||||
if (setProperties) result[i].setProperties = setProperties;
|
||||
const assertedProperties = this.assertedProperties[pattern];
|
||||
if (assertedProperties) result[i].assertedProperties = assertedProperties;
|
||||
const refutedProperties = this.refutedProperties[pattern];
|
||||
if (refutedProperties) result[i].refutedProperties = refutedProperties;
|
||||
}
|
||||
|
||||
const properties = this.patternProperties[pattern];
|
||||
if (properties) result[i].properties = properties;
|
||||
}
|
||||
|
||||
C._free(startAddress);
|
||||
|
|
@ -887,8 +976,12 @@ class Query {
|
|||
|
||||
if (this.predicates[pattern].every(p => p(captures))) {
|
||||
const capture = captures[captureIndex];
|
||||
const properties = this.patternProperties[pattern];
|
||||
if (properties) capture.properties = properties;
|
||||
const setProperties = this.setProperties[pattern];
|
||||
if (setProperties) capture.setProperties = setProperties;
|
||||
const assertedProperties = this.assertedProperties[pattern];
|
||||
if (assertedProperties) capture.assertedProperties = assertedProperties;
|
||||
const refutedProperties = this.refutedProperties[pattern];
|
||||
if (refutedProperties) capture.refutedProperties = refutedProperties;
|
||||
result.push(capture);
|
||||
}
|
||||
}
|
||||
|
|
@ -898,77 +991,6 @@ class Query {
|
|||
}
|
||||
}
|
||||
|
||||
function buildQueryPredicate(steps) {
|
||||
if (steps[0].type !== 'string') {
|
||||
throw new Error('Predicates must begin with a literal value');
|
||||
}
|
||||
|
||||
switch (steps[0].value) {
|
||||
case 'eq?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`eq?\` predicate. Expected 2, got ${steps.length - 1}`
|
||||
);
|
||||
if (steps[1].type !== 'capture') throw new Error(
|
||||
`First argument of \`eq?\` predicate must be a capture. Got "${steps[1].value}"`
|
||||
);
|
||||
if (steps[2].type === 'capture') {
|
||||
const captureName1 = steps[1].name;
|
||||
const captureName2 = steps[2].name;
|
||||
return function(captures) {
|
||||
let node1, node2
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName1) node1 = c.node;
|
||||
if (c.name === captureName2) node2 = c.node;
|
||||
}
|
||||
return node1.text === node2.text
|
||||
}
|
||||
} else {
|
||||
const captureName = steps[1].name;
|
||||
const stringValue = steps[2].value;
|
||||
return function(captures) {
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName) return c.node.text === stringValue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
case 'match?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`match?\` predicate. Expected 2, got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps[1].type !== 'capture') throw new Error(
|
||||
`First argument of \`match?\` predicate must be a capture. Got "${steps[1].value}".`
|
||||
);
|
||||
if (steps[2].type !== 'string') throw new Error(
|
||||
`Second argument of \`match?\` predicate must be a string. Got @${steps[2].value}.`
|
||||
);
|
||||
const captureName = steps[1].name;
|
||||
const regex = new RegExp(steps[2].value);
|
||||
return function(captures) {
|
||||
for (const c of captures) {
|
||||
if (c.name === captureName) return regex.test(c.node.text);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case 'set!':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`set!\` predicate. Expected 2, got ${steps.length - 1}.`
|
||||
);
|
||||
if (steps[1].type !== 'string' || steps[2].type !== 'string') throw new Error(
|
||||
`Arguments to \`set!\` predicate must be a strings.".`
|
||||
);
|
||||
return {
|
||||
key: steps[1].value,
|
||||
value: steps[2].value,
|
||||
};
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown query predicate \`${steps[0].value}\``);
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalCaptures(query, tree, address, result) {
|
||||
for (let i = 0, n = result.length; i < n; i++) {
|
||||
const captureIndex = getValue(address, 'i32');
|
||||
|
|
|
|||
|
|
@ -202,16 +202,18 @@ describe("Query", () => {
|
|||
tree = parser.parse(`a(b.c);`);
|
||||
query = JavaScript.query(`
|
||||
((call_expression (identifier) @func)
|
||||
(set! foo bar)
|
||||
(set! baz quux))
|
||||
(set! foo)
|
||||
(set! bar baz))
|
||||
|
||||
(property_identifier) @prop
|
||||
((property_identifier) @prop
|
||||
(is? foo)
|
||||
(is-not? bar baz))
|
||||
`);
|
||||
|
||||
const captures = query.captures(tree.rootNode);
|
||||
assert.deepEqual(formatCaptures(captures), [
|
||||
{name: 'func', text: 'a', properties: {foo: 'bar', baz: 'quux'}},
|
||||
{name: 'prop', text: 'c'},
|
||||
{name: 'func', text: 'a', setProperties: {foo: null, bar: 'baz'}},
|
||||
{name: 'prop', text: 'c', assertedProperties: {foo: null}, refutedProperties: {bar: 'baz'}},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue