Fix wasm query tests
This commit is contained in:
parent
f6f96f3503
commit
fce5c50f81
3 changed files with 123 additions and 130 deletions
|
|
@ -748,10 +748,10 @@ class Language {
|
|||
isPositive = false;
|
||||
case 'eq?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`eq?\` predicate. Expected 2, got ${steps.length - 1}`
|
||||
`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}"`
|
||||
`First argument of \`#eq?\` predicate must be a capture. Got "${steps[1].value}"`
|
||||
);
|
||||
if (steps[2].type === 'capture') {
|
||||
const captureName1 = steps[1].name;
|
||||
|
|
@ -780,13 +780,13 @@ class Language {
|
|||
|
||||
case 'match?':
|
||||
if (steps.length !== 3) throw new Error(
|
||||
`Wrong number of arguments to \`match?\` predicate. Expected 2, got ${steps.length - 1}.`
|
||||
`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}".`
|
||||
`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}.`
|
||||
`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);
|
||||
|
|
@ -800,10 +800,10 @@ class Language {
|
|||
|
||||
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}.`
|
||||
`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.".`
|
||||
`Arguments to \`#set!\` predicate must be a strings.".`
|
||||
);
|
||||
if (!setProperties[i]) setProperties[i] = {};
|
||||
setProperties[i][steps[1].value] = steps[2] ? steps[2].value : null;
|
||||
|
|
@ -812,10 +812,10 @@ class Language {
|
|||
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}.`
|
||||
`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.".`
|
||||
`Arguments to \`#${operator}\` predicate must be a strings.".`
|
||||
);
|
||||
const properties = operator === 'is?' ? assertedProperties : refutedProperties;
|
||||
if (!properties[i]) properties[i] = {};
|
||||
|
|
@ -823,7 +823,7 @@ class Language {
|
|||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown query predicate \`${steps[0].value}\``);
|
||||
throw new Error(`Unknown query predicate \`#${steps[0].value}\``);
|
||||
}
|
||||
|
||||
steps.length = 0;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
const {assert} = require('chai');
|
||||
const { assert } = require("chai");
|
||||
let Parser, JavaScript;
|
||||
|
||||
describe("Query", () => {
|
||||
let parser, tree, query;
|
||||
|
||||
before(async () =>
|
||||
({Parser, JavaScript} = await require('./helper'))
|
||||
);
|
||||
before(async () => ({ Parser, JavaScript } = await require("./helper")));
|
||||
|
||||
beforeEach(() => {
|
||||
parser = new Parser().setLanguage(JavaScript);
|
||||
|
|
@ -18,81 +16,75 @@ describe("Query", () => {
|
|||
if (query) query.delete();
|
||||
});
|
||||
|
||||
describe('construction', () => {
|
||||
it('throws an error on invalid patterns', () => {
|
||||
describe("construction", () => {
|
||||
it("throws an error on invalid patterns", () => {
|
||||
assert.throws(() => {
|
||||
JavaScript.query("(function_declaration wat)")
|
||||
}, "Bad syntax at offset 22: \'wat)\'...");
|
||||
JavaScript.query("(function_declaration wat)");
|
||||
}, "Bad syntax at offset 22: 'wat)'...");
|
||||
assert.throws(() => {
|
||||
JavaScript.query("(non_existent)")
|
||||
JavaScript.query("(non_existent)");
|
||||
}, "Bad node name 'non_existent'");
|
||||
assert.throws(() => {
|
||||
JavaScript.query("(a)")
|
||||
JavaScript.query("(a)");
|
||||
}, "Bad node name 'a'");
|
||||
assert.throws(() => {
|
||||
JavaScript.query("(function_declaration non_existent:(identifier))")
|
||||
JavaScript.query("(function_declaration non_existent:(identifier))");
|
||||
}, "Bad field name 'non_existent'");
|
||||
});
|
||||
|
||||
it('throws an error on invalid predicates', () => {
|
||||
it("throws an error on invalid predicates", () => {
|
||||
assert.throws(() => {
|
||||
JavaScript.query("((identifier) @abc (eq? @ab hi))")
|
||||
JavaScript.query("((identifier) @abc (#eq? @ab hi))");
|
||||
}, "Bad capture name @ab");
|
||||
assert.throws(() => {
|
||||
JavaScript.query("((identifier) @abc (eq? @ab hi))")
|
||||
JavaScript.query("((identifier) @abc (#eq? @ab hi))");
|
||||
}, "Bad capture name @ab");
|
||||
assert.throws(() => {
|
||||
JavaScript.query("((identifier) @abc (eq?))")
|
||||
}, "Wrong number of arguments to `eq?` predicate. Expected 2, got 0");
|
||||
JavaScript.query("((identifier) @abc (#eq?))");
|
||||
}, "Wrong number of arguments to `#eq?` predicate. Expected 2, got 0");
|
||||
assert.throws(() => {
|
||||
JavaScript.query("((identifier) @a (eq? @a @a @a))")
|
||||
}, "Wrong number of arguments to `eq?` predicate. Expected 2, got 3");
|
||||
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?`");
|
||||
JavaScript.query("((identifier) @a (#something-else? @a))");
|
||||
}, "Unknown query predicate `#something-else?`");
|
||||
});
|
||||
});
|
||||
|
||||
describe('.matches', () => {
|
||||
it('returns all of the matches for the given query', () => {
|
||||
describe(".matches", () => {
|
||||
it("returns all of the matches for the given query", () => {
|
||||
tree = parser.parse("function one() { two(); function three() {} }");
|
||||
query = JavaScript.query(`
|
||||
(function_declaration name:(identifier) @fn-def)
|
||||
(call_expression function:(identifier) @fn-ref)
|
||||
(function_declaration name: (identifier) @fn-def)
|
||||
(call_expression function: (identifier) @fn-ref)
|
||||
`);
|
||||
const matches = query.matches(tree.rootNode);
|
||||
assert.deepEqual(
|
||||
formatMatches(matches),
|
||||
[
|
||||
{pattern: 0, captures: [{name: 'fn-def', text: 'one'}]},
|
||||
{pattern: 1, captures: [{name: 'fn-ref', text: 'two'}]},
|
||||
{pattern: 0, captures: [{name: 'fn-def', text: 'three'}]},
|
||||
]
|
||||
);
|
||||
assert.deepEqual(formatMatches(matches), [
|
||||
{ pattern: 0, captures: [{ name: "fn-def", text: "one" }] },
|
||||
{ pattern: 1, captures: [{ name: "fn-ref", text: "two" }] },
|
||||
{ pattern: 0, captures: [{ name: "fn-def", text: "three" }] },
|
||||
]);
|
||||
});
|
||||
|
||||
it('can search in a specified ranges', () => {
|
||||
it("can search in a specified ranges", () => {
|
||||
tree = parser.parse("[a, b,\nc, d,\ne, f,\ng, h]");
|
||||
query = JavaScript.query('(identifier) @element');
|
||||
query = JavaScript.query("(identifier) @element");
|
||||
const matches = query.matches(
|
||||
tree.rootNode,
|
||||
{row: 1, column: 1},
|
||||
{row: 3, column: 1}
|
||||
);
|
||||
assert.deepEqual(
|
||||
formatMatches(matches),
|
||||
[
|
||||
{pattern: 0, captures: [{name: 'element', text: 'd'}]},
|
||||
{pattern: 0, captures: [{name: 'element', text: 'e'}]},
|
||||
{pattern: 0, captures: [{name: 'element', text: 'f'}]},
|
||||
{pattern: 0, captures: [{name: 'element', text: 'g'}]},
|
||||
]
|
||||
{ row: 1, column: 1 },
|
||||
{ row: 3, column: 1 }
|
||||
);
|
||||
assert.deepEqual(formatMatches(matches), [
|
||||
{ pattern: 0, captures: [{ name: "element", text: "d" }] },
|
||||
{ pattern: 0, captures: [{ name: "element", text: "e" }] },
|
||||
{ pattern: 0, captures: [{ name: "element", text: "f" }] },
|
||||
{ pattern: 0, captures: [{ name: "element", text: "g" }] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.captures', () => {
|
||||
it('returns all of the captures for the given query, in order', () => {
|
||||
describe(".captures", () => {
|
||||
it("returns all of the captures for the given query, in order", () => {
|
||||
tree = parser.parse(`
|
||||
a({
|
||||
bc: function de() {
|
||||
|
|
@ -105,12 +97,12 @@ describe("Query", () => {
|
|||
`);
|
||||
query = JavaScript.query(`
|
||||
(pair
|
||||
key: * @method.def
|
||||
key: _ @method.def
|
||||
(function
|
||||
name: (identifier) @method.alias))
|
||||
|
||||
(variable_declarator
|
||||
name: * @function.def
|
||||
name: _ @function.def
|
||||
value: (function
|
||||
name: (identifier) @function.alias))
|
||||
|
||||
|
|
@ -119,26 +111,23 @@ describe("Query", () => {
|
|||
`);
|
||||
|
||||
const captures = query.captures(tree.rootNode);
|
||||
assert.deepEqual(
|
||||
formatCaptures(captures),
|
||||
[
|
||||
{name: "method.def", text: "bc"},
|
||||
{name: "delimiter", text: ":"},
|
||||
{name: "method.alias", text: "de"},
|
||||
{name: "function.def", text: "fg"},
|
||||
{name: "operator", text: "="},
|
||||
{name: "function.alias", text: "hi"},
|
||||
{name: "method.def", text: "jk"},
|
||||
{name: "delimiter", text: ":"},
|
||||
{name: "method.alias", text: "lm"},
|
||||
{name: "function.def", text: "no"},
|
||||
{name: "operator", text: "="},
|
||||
{name: "function.alias", text: "pq"},
|
||||
]
|
||||
);
|
||||
assert.deepEqual(formatCaptures(captures), [
|
||||
{ name: "method.def", text: "bc" },
|
||||
{ name: "delimiter", text: ":" },
|
||||
{ name: "method.alias", text: "de" },
|
||||
{ name: "function.def", text: "fg" },
|
||||
{ name: "operator", text: "=" },
|
||||
{ name: "function.alias", text: "hi" },
|
||||
{ name: "method.def", text: "jk" },
|
||||
{ name: "delimiter", text: ":" },
|
||||
{ name: "method.alias", text: "lm" },
|
||||
{ name: "function.def", text: "no" },
|
||||
{ name: "operator", text: "=" },
|
||||
{ name: "function.alias", text: "pq" },
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles conditions that compare the text of capture to literal strings', () => {
|
||||
it("handles conditions that compare the text of capture to literal strings", () => {
|
||||
tree = parser.parse(`
|
||||
const ab = require('./ab');
|
||||
new Cd(EF);
|
||||
|
|
@ -148,32 +137,29 @@ describe("Query", () => {
|
|||
(identifier) @variable
|
||||
|
||||
((identifier) @function.builtin
|
||||
(eq? @function.builtin "require"))
|
||||
(#eq? @function.builtin "require"))
|
||||
|
||||
((identifier) @constructor
|
||||
(match? @constructor "^[A-Z]"))
|
||||
(#match? @constructor "^[A-Z]"))
|
||||
|
||||
((identifier) @constant
|
||||
(match? @constant "^[A-Z]{2,}$"))
|
||||
(#match? @constant "^[A-Z]{2,}$"))
|
||||
`);
|
||||
|
||||
const captures = query.captures(tree.rootNode);
|
||||
assert.deepEqual(
|
||||
formatCaptures(captures),
|
||||
[
|
||||
{name: "variable", text: "ab"},
|
||||
{name: "variable", text: "require"},
|
||||
{name: "function.builtin", text: "require"},
|
||||
{name: "variable", text: "Cd"},
|
||||
{name: "constructor", text: "Cd"},
|
||||
{name: "variable", text: "EF"},
|
||||
{name: "constructor", text: "EF"},
|
||||
{name: "constant", text: "EF"},
|
||||
]
|
||||
);
|
||||
assert.deepEqual(formatCaptures(captures), [
|
||||
{ name: "variable", text: "ab" },
|
||||
{ name: "variable", text: "require" },
|
||||
{ name: "function.builtin", text: "require" },
|
||||
{ name: "variable", text: "Cd" },
|
||||
{ name: "constructor", text: "Cd" },
|
||||
{ name: "variable", text: "EF" },
|
||||
{ name: "constructor", text: "EF" },
|
||||
{ name: "constant", text: "EF" },
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles conditions that compare the text of capture to each other', () => {
|
||||
it("handles conditions that compare the text of capture to each other", () => {
|
||||
tree = parser.parse(`
|
||||
ab = abc + 1;
|
||||
def = de + 1;
|
||||
|
|
@ -181,56 +167,60 @@ describe("Query", () => {
|
|||
`);
|
||||
|
||||
query = JavaScript.query(`
|
||||
((assignment_expression
|
||||
(
|
||||
(assignment_expression
|
||||
left: (identifier) @id1
|
||||
right: (binary_expression
|
||||
left: (identifier) @id2))
|
||||
(eq? @id1 @id2))
|
||||
`);
|
||||
|
||||
const captures = query.captures(tree.rootNode);
|
||||
assert.deepEqual(
|
||||
formatCaptures(captures),
|
||||
[
|
||||
{name: "id1", text: "ghi"},
|
||||
{name: "id2", text: "ghi"},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
it('handles patterns with properties', () => {
|
||||
tree = parser.parse(`a(b.c);`);
|
||||
query = JavaScript.query(`
|
||||
((call_expression (identifier) @func)
|
||||
(set! foo)
|
||||
(set! bar baz))
|
||||
|
||||
((property_identifier) @prop
|
||||
(is? foo)
|
||||
(is-not? bar baz))
|
||||
(#eq? @id1 @id2)
|
||||
)
|
||||
`);
|
||||
|
||||
const captures = query.captures(tree.rootNode);
|
||||
assert.deepEqual(formatCaptures(captures), [
|
||||
{name: 'func', text: 'a', setProperties: {foo: null, bar: 'baz'}},
|
||||
{name: 'prop', text: 'c', assertedProperties: {foo: null}, refutedProperties: {bar: 'baz'}},
|
||||
{ name: "id1", text: "ghi" },
|
||||
{ name: "id2", text: "ghi" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("handles patterns with properties", () => {
|
||||
tree = parser.parse(`a(b.c);`);
|
||||
query = JavaScript.query(`
|
||||
((call_expression (identifier) @func)
|
||||
(#set! foo)
|
||||
(#set! bar baz))
|
||||
|
||||
((property_identifier) @prop
|
||||
(#is? foo)
|
||||
(#is-not? bar baz))
|
||||
`);
|
||||
|
||||
const captures = query.captures(tree.rootNode);
|
||||
assert.deepEqual(formatCaptures(captures), [
|
||||
{ name: "func", text: "a", setProperties: { foo: null, bar: "baz" } },
|
||||
{
|
||||
name: "prop",
|
||||
text: "c",
|
||||
assertedProperties: { foo: null },
|
||||
refutedProperties: { bar: "baz" },
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function formatMatches(matches) {
|
||||
return matches.map(({pattern, captures}) => ({
|
||||
return matches.map(({ pattern, captures }) => ({
|
||||
pattern,
|
||||
captures: formatCaptures(captures)
|
||||
}))
|
||||
captures: formatCaptures(captures),
|
||||
}));
|
||||
}
|
||||
|
||||
function formatCaptures(captures) {
|
||||
return captures.map(c => {
|
||||
return captures.map((c) => {
|
||||
const node = c.node;
|
||||
delete c.node;
|
||||
c.text = node.text;
|
||||
return c;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -581,10 +581,8 @@ static TSQueryError ts_query__parse_predicate(
|
|||
.type = TSQueryPredicateStepTypeString,
|
||||
.value_id = id,
|
||||
}));
|
||||
stream_advance(stream);
|
||||
stream_skip_whitespace(stream);
|
||||
|
||||
unsigned step_count = 0;
|
||||
for (;;) {
|
||||
if (stream->next == ')') {
|
||||
stream_advance(stream);
|
||||
|
|
@ -689,7 +687,6 @@ static TSQueryError ts_query__parse_predicate(
|
|||
return TSQueryErrorSyntax;
|
||||
}
|
||||
|
||||
step_count++;
|
||||
stream_skip_whitespace(stream);
|
||||
}
|
||||
|
||||
|
|
@ -765,7 +762,7 @@ static TSQueryError ts_query__parse_pattern(
|
|||
stream->next == '_' ||
|
||||
|
||||
// TODO - remove.
|
||||
// For temporary backward compatibility, handle parenthesized '*' as a wildcard.
|
||||
// For temporary backward compatibility, handle '*' as a wildcard.
|
||||
stream->next == '*'
|
||||
) {
|
||||
symbol = depth > 0 ? NAMED_WILDCARD_SYMBOL : WILDCARD_SYMBOL;
|
||||
|
|
@ -836,7 +833,13 @@ static TSQueryError ts_query__parse_pattern(
|
|||
}
|
||||
|
||||
// Parse a wildcard pattern
|
||||
else if (stream->next == '_') {
|
||||
else if (
|
||||
stream->next == '_' ||
|
||||
|
||||
// TODO remove.
|
||||
// For temporary backward compatibility, handle '*' as a wildcard.
|
||||
stream->next == '*'
|
||||
) {
|
||||
stream_advance(stream);
|
||||
stream_skip_whitespace(stream);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue