wasm: Allow arbitrary predicates in queries

This commit is contained in:
Max Brunsfeld 2020-06-01 13:28:52 -07:00
parent 47d607da8d
commit 28a779d6a0
2 changed files with 66 additions and 10 deletions

View file

@ -44,6 +44,8 @@ class Parser {
delete() {
C._ts_parser_delete(this[0]);
C._free(this[1]);
this[0] = 0;
this[1] = 0;
}
setLanguage(language) {
@ -163,6 +165,7 @@ class Tree {
delete() {
C._ts_tree_delete(this[0]);
this[0] = 0;
}
edit(edit) {
@ -506,6 +509,7 @@ class TreeCursor {
delete() {
marshalTreeCursor(this);
C._ts_tree_cursor_delete_wasm(this.tree[0]);
this[0] = this[1] = this[2] = 0;
}
reset(node) {
@ -716,6 +720,7 @@ class Language {
const assertedProperties = new Array(patternCount);
const refutedProperties = new Array(patternCount);
const predicates = new Array(patternCount);
const textPredicates = new Array(patternCount);
for (let i = 0; i < patternCount; i++) {
const predicatesAddress = C._ts_query_predicates_for_pattern(
address,
@ -725,6 +730,7 @@ class Language {
const stepCount = getValue(TRANSFER_BUFFER, 'i32');
predicates[i] = [];
textPredicates[i] = [];
const steps = [];
let stepAddress = predicatesAddress;
@ -756,7 +762,7 @@ class Language {
if (steps[2].type === 'capture') {
const captureName1 = steps[1].name;
const captureName2 = steps[2].name;
predicates[i].push(function(captures) {
textPredicates[i].push(function(captures) {
let node1, node2
for (const c of captures) {
if (c.name === captureName1) node1 = c.node;
@ -767,7 +773,7 @@ class Language {
} else {
const captureName = steps[1].name;
const stringValue = steps[2].value;
predicates[i].push(function(captures) {
textPredicates[i].push(function(captures) {
for (const c of captures) {
if (c.name === captureName) {
return (c.node.text === stringValue) === isPositive;
@ -790,7 +796,7 @@ class Language {
);
const captureName = steps[1].name;
const regex = new RegExp(steps[2].value);
predicates[i].push(function(captures) {
textPredicates[i].push(function(captures) {
for (const c of captures) {
if (c.name === captureName) return regex.test(c.node.text);
}
@ -823,7 +829,7 @@ class Language {
break;
default:
throw new Error(`Unknown query predicate \`#${steps[0].value}\``);
predicates[i].push({operator, operands: steps.slice(1)});
}
steps.length = 0;
@ -840,6 +846,7 @@ class Language {
INTERNAL,
address,
captureNames,
textPredicates,
predicates,
Object.freeze(setProperties),
Object.freeze(assertedProperties),
@ -888,12 +895,13 @@ class Language {
class Query {
constructor(
internal, address, captureNames, predicates,
internal, address, captureNames, textPredicates, predicates,
setProperties, assertedProperties, refutedProperties
) {
assertInternal(internal);
this[0] = address;
this.captureNames = captureNames;
this.textPredicates = textPredicates;
this.predicates = predicates;
this.setProperties = setProperties;
this.assertedProperties = assertedProperties;
@ -902,6 +910,7 @@ class Query {
delete() {
C._ts_query_delete(this[0]);
this[0] = 0;
}
matches(node, startPosition, endPosition) {
@ -932,7 +941,7 @@ class Query {
const captures = new Array(captureCount);
address = unmarshalCaptures(this, node.tree, address, captures);
if (this.predicates[pattern].every(p => p(captures))) {
if (this.textPredicates[pattern].every(p => p(captures))) {
result[i] = {pattern, captures};
const setProperties = this.setProperties[pattern];
if (setProperties) result[i].setProperties = setProperties;
@ -979,7 +988,7 @@ class Query {
captures.length = captureCount
address = unmarshalCaptures(this, node.tree, address, captures);
if (this.predicates[pattern].every(p => p(captures))) {
if (this.textPredicates[pattern].every(p => p(captures))) {
const capture = captures[captureIndex];
const setProperties = this.setProperties[pattern];
if (setProperties) capture.setProperties = setProperties;
@ -994,6 +1003,10 @@ class Query {
C._free(startAddress);
return result;
}
predicatesForPattern(patternIndex) {
return this.predicates[patternIndex]
}
}
function getText(tree, startIndex, endIndex) {

View file

@ -45,9 +45,6 @@ describe("Query", () => {
assert.throws(() => {
JavaScript.query("((identifier) @a (eq? @a @a @a))");
}, "Wrong number of arguments to `#eq?` predicate. Expected 2, got 3");
assert.throws(() => {
JavaScript.query("((identifier) @a (#something-else? @a))");
}, "Unknown query predicate `#something-else?`");
});
});
@ -207,6 +204,52 @@ describe("Query", () => {
]);
});
});
describe(".predicatesForPattern(index)", () => {
it("returns all of the predicates as objects", () => {
query = JavaScript.query(`
(
(binary_expression
left: (identifier) @a
right: (identifier) @b)
(#something? @a @b)
(#match? @a "c")
(#something-else? @a "A" @b "B")
)
((identifier) @c
(#hello! @c))
"if" @d
`);
assert.deepEqual(query.predicatesForPattern(0), [
{
operator: "something?",
operands: [
{ type: "capture", name: "a" },
{ type: "capture", name: "b" },
],
},
{
operator: "something-else?",
operands: [
{ type: "capture", name: "a" },
{ type: "string", value: "A" },
{ type: "capture", name: "b" },
{ type: "string", value: "B" },
],
},
]);
assert.deepEqual(query.predicatesForPattern(1), [
{
operator: "hello!",
operands: [{ type: "capture", name: "c" }],
},
]);
assert.deepEqual(query.predicatesForPattern(2), []);
});
});
});
function formatMatches(matches) {