From fce5c50f81f0b070f0ac1faf0f4e23ba19198d3b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 11 May 2020 15:22:05 -0700 Subject: [PATCH] Fix wasm query tests --- lib/binding_web/binding.js | 20 +-- lib/binding_web/test/query-test.js | 220 ++++++++++++++--------------- lib/src/query.c | 13 +- 3 files changed, 123 insertions(+), 130 deletions(-) diff --git a/lib/binding_web/binding.js b/lib/binding_web/binding.js index f52f61d5..feedb37f 100644 --- a/lib/binding_web/binding.js +++ b/lib/binding_web/binding.js @@ -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; diff --git a/lib/binding_web/test/query-test.js b/lib/binding_web/test/query-test.js index b5a37ed9..8683214a 100644 --- a/lib/binding_web/test/query-test.js +++ b/lib/binding_web/test/query-test.js @@ -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; - }) + }); } diff --git a/lib/src/query.c b/lib/src/query.c index 72bf04a8..5c06ed0f 100644 --- a/lib/src/query.c +++ b/lib/src/query.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);