├── .npmrc
├── .gitignore
├── .eslintignore
├── .babelrc.json
├── tests
├── fixtures
│ ├── forLoop.js
│ ├── bigArray.js
│ ├── whileLoop.js
│ ├── simpleProgram.js
│ ├── simpleFunction.js
│ ├── conditional.js
│ ├── nestedFunctions.js
│ ├── literal.js
│ ├── switchStatement.js
│ ├── unknownNodeTypeAST.js
│ ├── conditionalLong.js
│ ├── customNodes.js
│ ├── customNodesWithKind.js
│ └── allClasses.js
├── unknownNodeType.js
├── queryCompound.js
├── parser.js
├── queryDescendant.js
├── traverse.js
├── queryField.js
├── queryHas.js
├── queryComplex.js
├── queryNot.js
├── match.js
├── queryWildcard.js
├── queryMatches.js
├── queryType.js
├── queryClass.js
├── querySubject.js
├── matches.js
├── queryPseudoChild.js
└── queryAttribute.js
├── .editorconfig
├── testRunner.html
├── .eslintrc.js
├── .github
└── workflows
│ └── NodeCI.yml
├── license.txt
├── rollup.config.js
├── README.md
├── package.json
├── grammar.pegjs
├── esquery.js
└── parser.js
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock = false
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.nyc_output
2 | /node_modules
3 | /coverage
4 | /dist
5 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | parser.js
4 |
5 | !.eslintrc.js
6 |
--------------------------------------------------------------------------------
/.babelrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env"]
4 | ],
5 | "plugins": [
6 | ["transform-es2017-object-entries"]
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/tests/fixtures/forLoop.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse('for (i = 0; i < foo.length; i++) { foo[i](); }');
4 |
5 | export default parsed;
6 |
--------------------------------------------------------------------------------
/tests/fixtures/bigArray.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse(
4 | '[1, 2, 3, foo, bar, 4, 5, baz, qux, 6]'
5 | );
6 |
7 | export default parsed;
8 |
--------------------------------------------------------------------------------
/tests/fixtures/whileLoop.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse(`
4 | x = 10;
5 | while (x > 0) { x--; }
6 | `);
7 |
8 | export default parsed;
9 |
--------------------------------------------------------------------------------
/tests/fixtures/simpleProgram.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse(`
4 | var x = 1;
5 | var y = 'y';
6 | x = x * 2;
7 | if (y) { y += 'z'; }
8 | `);
9 |
10 | export default parsed;
11 |
--------------------------------------------------------------------------------
/tests/fixtures/simpleFunction.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse(`
4 | function foo(x, y) {
5 | var z = x + y;
6 | z++;
7 | return z;
8 | }
9 | `);
10 |
11 | export default parsed;
12 |
--------------------------------------------------------------------------------
/tests/fixtures/conditional.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse(`
4 | if (x === 1) { foo(); } else { x = 2; }
5 | if (x == 'test' && true || x) { y = -1; } else if (false) { y = 1; }
6 | `);
7 |
8 | export default parsed;
9 |
--------------------------------------------------------------------------------
/tests/fixtures/nestedFunctions.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse(`
4 | function foo() {
5 | var x = 1;
6 | function bar() {
7 | x = 2;
8 | }
9 | }
10 | `);
11 |
12 | export default parsed;
13 |
--------------------------------------------------------------------------------
/tests/fixtures/literal.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse(`
4 | var y = '\b\f\\n\\r\t\v and just a back\\slash';
5 | var x = 21.35;
6 | var z = '\\z';
7 | var a = 'abc\\z';
8 | `);
9 |
10 | export default parsed;
11 |
--------------------------------------------------------------------------------
/tests/fixtures/switchStatement.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse(`
4 | var x = 1;
5 | switch (x) {
6 | case 0: foo1(); break;
7 | case 1: foo2(); break;
8 | default: x = 1; break;
9 | }
10 | `);
11 |
12 | export default parsed;
13 |
--------------------------------------------------------------------------------
/tests/unknownNodeType.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import AST from './fixtures/unknownNodeTypeAST.js';
3 |
4 | describe('Unknown node type', function () {
5 | it('does not throw', function () {
6 | try {
7 | esquery(AST, '*');
8 | } catch (e) {
9 | assert.fail();
10 | }
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/tests/fixtures/unknownNodeTypeAST.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This pretends (badly) to be a TypeAlias type node, which would normally be
3 | * generated by flow-parser from the following code:
4 | *
5 | * type aType = {};
6 | *
7 | */
8 | import * as esprima from 'esprima';
9 |
10 | const program = esprima.parse('var x = \'s\'');
11 | program.body[0].type = 'TypeAlias';
12 |
13 | export default program;
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = lf
8 | charset = utf-8
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 | indent_style = space
12 | indent_size = 4
13 |
14 | [*.json]
15 | indent_size = 2
16 |
17 | [*.pegjs]
18 | indent_size = 2
19 |
20 | [*.yml]
21 | indent_size = 2
22 |
--------------------------------------------------------------------------------
/tests/fixtures/conditionalLong.js:
--------------------------------------------------------------------------------
1 | import * as esprima from 'esprima';
2 |
3 | const parsed = esprima.parse(`
4 | if (x === 1) { foo(1); }
5 | if (x === 2) { foo(2); }
6 | if (x === 3) { foo(3); }
7 | if (x === 4) { foo(4); }
8 | if (x === 5) { foo(5); }
9 | if (x === 6) { foo(6); }
10 | if (x === 7) { foo(7); }
11 | if (x === 8) { foo(8); }
12 | if (x === 9) { foo(9); }
13 | if (x === 10) { foo(10); }
14 | if (x === 11) { foo(11); }
15 | `);
16 |
17 | export default parsed;
18 |
--------------------------------------------------------------------------------
/tests/queryCompound.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 |
4 | describe('Compound query', function () {
5 |
6 | it('two attributes', function () {
7 | const matches = esquery(conditional, '[left.name="x"][right.value=1]');
8 | assert.includeMembers(matches, [
9 | conditional.body[0].test
10 | ]);
11 | });
12 |
13 | it('type and pseudo', function () {
14 | const matches = esquery(conditional, '[left.name="x"]:matches(*)');
15 | assert.includeMembers(matches, [
16 | conditional.body[0].test
17 | ]);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/tests/parser.js:
--------------------------------------------------------------------------------
1 | import esquery from "../esquery.js";
2 |
3 | describe("basic query parsing", function () {
4 |
5 | it("empty query", function () {
6 | assert.equal(void 0, esquery.parse(""));
7 | assert.equal(void 0, esquery.parse(" "));
8 | });
9 |
10 | it("leading/trailing whitespace", function () {
11 | assert.notEqual(void 0, esquery.parse(" A"));
12 | assert.notEqual(void 0, esquery.parse(" A"));
13 | assert.notEqual(void 0, esquery.parse("A "));
14 | assert.notEqual(void 0, esquery.parse("A "));
15 | assert.notEqual(void 0, esquery.parse(" A "));
16 | assert.notEqual(void 0, esquery.parse(" A "));
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/tests/fixtures/customNodes.js:
--------------------------------------------------------------------------------
1 | export default {
2 | type: 'CustomRoot',
3 | list: [
4 | {
5 | type: 'CustomChild',
6 | name: 'one',
7 | sublist: [{ type: 'CustomGrandChild' }],
8 | },
9 | {
10 | type: 'CustomChild',
11 | name: 'two',
12 | sublist: [],
13 | },
14 | {
15 | type: 'CustomChild',
16 | name: 'three',
17 | sublist: [
18 | { type: 'CustomGrandChild' },
19 | { type: 'CustomGrandChild' },
20 | ],
21 | },
22 | {
23 | type: 'CustomChild',
24 | name: 'four',
25 | sublist: [
26 | { type: 'CustomGrandChild' },
27 | { type: 'CustomGrandChild' },
28 | { type: 'CustomGrandChild' },
29 | ],
30 | },
31 | ],
32 | };
33 |
--------------------------------------------------------------------------------
/tests/queryDescendant.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 |
4 | describe('Pseudo matches query', function () {
5 |
6 | it('conditional matches', function () {
7 | const matches = esquery(conditional, 'Program IfStatement');
8 | assert.includeMembers(matches, [
9 | conditional.body[0],
10 | conditional.body[1],
11 | conditional.body[1].alternate
12 | ]);
13 | });
14 |
15 | it('#8: descendant selector includes ancestor in search', function() {
16 | let matches = esquery(conditional, 'Identifier[name=x]');
17 | assert.equal(4, matches.length);
18 | matches = esquery(conditional, 'Identifier [name=x]');
19 | assert.equal(0, matches.length);
20 | matches = esquery(conditional, 'BinaryExpression [name=x]');
21 | assert.equal(2, matches.length);
22 | matches = esquery(conditional, 'AssignmentExpression [name=x]');
23 | assert.equal(1, matches.length);
24 | });
25 |
26 | });
27 |
--------------------------------------------------------------------------------
/tests/fixtures/customNodesWithKind.js:
--------------------------------------------------------------------------------
1 | export default {
2 | kind: 'CustomRoot',
3 | list: [
4 | {
5 | kind: 'CustomChild',
6 | name: 'one',
7 | sublist: [{ kind: 'CustomGrandChild' }],
8 | },
9 | {
10 | kind: 'CustomChild',
11 | name: 'two',
12 | sublist: [],
13 | },
14 | {
15 | kind: 'CustomChild',
16 | name: 'three',
17 | sublist: [
18 | { kind: 'CustomGrandChild' },
19 | { kind: 'CustomGrandChild' },
20 | ],
21 | },
22 | {
23 | kind: 'CustomChild',
24 | name: 'four',
25 | sublist: [
26 | { kind: 'CustomGrandChild' },
27 | { kind: 'CustomGrandChild' },
28 | { kind: 'CustomGrandChild' },
29 | ],
30 | },
31 | {
32 | kind: 'CustomExpression'
33 | },
34 | {
35 | kind: 'CustomStatement'
36 | }
37 | ],
38 | };
39 |
--------------------------------------------------------------------------------
/tests/traverse.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 |
4 | describe('traverse', function () {
5 | it('iterates matches', function () {
6 | const matches = [];
7 | const parents = [];
8 | const ancestries = [];
9 | const selector = esquery.parse(':matches(IfStatement)');
10 | esquery.traverse(conditional, selector, (match, parent, ancestry) => {
11 | parents.push(parent);
12 | matches.push(match);
13 | ancestries.push(ancestry.slice());
14 | });
15 | assert.deepEqual(matches, [
16 | conditional.body[0],
17 | conditional.body[1],
18 | conditional.body[1].alternate
19 | ]);
20 | assert.deepEqual(parents, [
21 | conditional,
22 | conditional,
23 | conditional.body[1]
24 | ]);
25 | assert.deepEqual(ancestries, [
26 | [conditional],
27 | [conditional],
28 | [
29 | conditional.body[1],
30 | conditional
31 | ]
32 | ]);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/tests/queryField.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 | import simpleProgram from './fixtures/simpleProgram.js';
4 |
5 | describe('Field query', function () {
6 |
7 | it('single field', function () {
8 | const matches = esquery(conditional, '.test');
9 | assert.includeMembers(matches, [
10 | conditional.body[0].test,
11 | conditional.body[1].test,
12 | conditional.body[1].alternate.test
13 | ]);
14 | });
15 |
16 | it('field sequence', function () {
17 | const matches = esquery(simpleProgram, '.declarations.init');
18 | assert.includeMembers(matches, [
19 | simpleProgram.body[0].declarations[0].init,
20 | simpleProgram.body[1].declarations[0].init
21 | ]);
22 | });
23 |
24 | it('field sequence (long)', function () {
25 | const matches = esquery(simpleProgram, '.body.declarations.init');
26 | assert.includeMembers(matches, [
27 | simpleProgram.body[0].declarations[0].init,
28 | simpleProgram.body[1].declarations[0].init
29 | ]);
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/testRunner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Test Runner
5 |
6 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | env: {
4 | browser: true,
5 | commonjs: true,
6 | es6: true,
7 | node: true
8 | },
9 | extends: 'eslint:recommended',
10 | globals: {
11 | Atomics: 'readonly',
12 | SharedArrayBuffer: 'readonly'
13 | },
14 | overrides: [{
15 | files: '.eslintrc.js',
16 | parserOptions: {
17 | sourceType: 'script'
18 | },
19 | rules: {
20 | strict: 'error'
21 | }
22 | }, {
23 | files: 'tests/**',
24 | globals: {
25 | assert: true
26 | },
27 | env: {
28 | mocha: true
29 | }
30 | }],
31 | parserOptions: {
32 | sourceType: 'module',
33 | ecmaVersion: 2018
34 | },
35 | rules: {
36 | semi: ['error'],
37 | indent: ['error', 4, { SwitchCase: 1 }],
38 | 'prefer-const': ['error'],
39 | 'no-var': ['error'],
40 | 'prefer-destructuring': ['error'],
41 | 'object-shorthand': ['error'],
42 | 'object-curly-spacing': ['error', 'always'],
43 | quotes: ['error', 'single'],
44 | 'quote-props': ['error', 'as-needed'],
45 | 'brace-style': ['error', '1tbs', { allowSingleLine: true }],
46 | 'prefer-template': ['error']
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/.github/workflows/NodeCI.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 | lint:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Use Node.js 12
15 | uses: actions/setup-node@v2
16 | with:
17 | node-version: 12
18 | - name: Install Packages
19 | run: npm install
20 | - name: Lint
21 | run: npm run lint
22 | test:
23 | runs-on: ubuntu-latest
24 | strategy:
25 | matrix:
26 | node-version: [10, 12, 18]
27 | steps:
28 | - uses: actions/checkout@v2
29 | - name: Use Node.js ${{ matrix.node-version }}
30 | uses: actions/setup-node@v2
31 | with:
32 | node-version: ${{ matrix.node-version }}
33 | - name: Install Packages
34 | run: npm install
35 | - name: Build
36 | run: npm run build
37 | - name: Test
38 | run: npm run test:ci
39 | test-with-node8:
40 | runs-on: ubuntu-latest
41 | steps:
42 | - uses: actions/checkout@v2
43 | - name: Use Node.js 8
44 | uses: actions/setup-node@v2
45 | with:
46 | node-version: 8
47 | - name: Install Packages
48 | run: |+
49 | npm install
50 | npm install --no-save "eslint@5"
51 | - name: Build
52 | run: npm run build
53 | - name: Test
54 | run: npm run test:ci
55 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013, Joel Feenstra
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * Neither the name of the ESQuery nor the names of its contributors may
12 | be used to endorse or promote products derived from this software without
13 | specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL JOEL FEENSTRA BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/tests/queryHas.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 |
4 | describe('Parent selector query', function () {
5 |
6 | it('conditional', function () {
7 | const matches = esquery(conditional, 'ExpressionStatement:has([name="foo"][type="Identifier"])');
8 | assert.equal(1, matches.length);
9 | });
10 |
11 | it('one of', function () {
12 | const matches = esquery(conditional, 'IfStatement:has(LogicalExpression [name="foo"], LogicalExpression [name="x"])');
13 | assert.equal(1, matches.length);
14 | });
15 |
16 | it('chaining', function () {
17 | const matches = esquery(conditional, 'BinaryExpression:has(Identifier[name="x"]):has(Literal[value="test"])');
18 | assert.equal(1, matches.length);
19 | });
20 |
21 | it('nesting', function () {
22 | const matches = esquery(conditional, 'Program:has(IfStatement:has(Literal[value=true], Literal[value=false]))');
23 | assert.equal(1, matches.length);
24 | });
25 |
26 | it('non-matching', function () {
27 | const matches = esquery(conditional, ':has([value="impossible"])');
28 | assert.equal(0, matches.length);
29 | });
30 |
31 | it('binary op', function () {
32 | const deepChildMatches = esquery(conditional, 'IfStatement:has(> Identifier[name="x"])');
33 | assert.equal(0, deepChildMatches.length);
34 |
35 | const shallowChildMatches = esquery(conditional, 'IfStatement:has(> LogicalExpression.test, > Identifier[name="x"])');
36 | assert.equal(1, shallowChildMatches.length);
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/tests/queryComplex.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 | import simpleProgram from './fixtures/simpleProgram.js';
4 |
5 | describe('Complex selector query', function () {
6 |
7 | it('two types child', function () {
8 | const matches = esquery(conditional, 'IfStatement > BinaryExpression');
9 | assert.includeMembers(matches, [
10 | conditional.body[0].test
11 | ]);
12 | });
13 |
14 | it('three types child', function () {
15 | const matches = esquery(conditional, 'IfStatement > BinaryExpression > Identifier');
16 | assert.includeMembers(matches, [
17 | conditional.body[0].test.left
18 | ]);
19 | });
20 |
21 | it('two types descendant', function () {
22 | const matches = esquery(conditional, 'IfStatement BinaryExpression');
23 | assert.includeMembers(matches, [
24 | conditional.body[0].test
25 | ]);
26 | });
27 |
28 | it('two types sibling', function () {
29 | const matches = esquery(simpleProgram, 'VariableDeclaration ~ IfStatement');
30 | assert.includeMembers(matches, [
31 | simpleProgram.body[3]
32 | ]);
33 | });
34 |
35 | it('two types adjacent', function () {
36 | const matches = esquery(simpleProgram, 'VariableDeclaration + ExpressionStatement');
37 | assert.includeMembers(matches, [
38 | simpleProgram.body[2]
39 | ]);
40 | });
41 |
42 | it('can not match a top level node', function () {
43 | // Test fix for issue #135: half of a child selector matches a top-level node.
44 | const matches = esquery(simpleProgram, 'NonExistingNodeType > *');
45 | assert.isEmpty(matches);
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/tests/queryNot.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 | import forLoop from './fixtures/forLoop.js';
4 | import simpleFunction from './fixtures/simpleFunction.js';
5 | import simpleProgram from './fixtures/simpleProgram.js';
6 |
7 | describe('Pseudo matches query', function () {
8 |
9 | it('conditional', function () {
10 | const matches = esquery(conditional, ':not(Literal)');
11 | assert.equal(28, matches.length);
12 | });
13 |
14 | it('for loop', function () {
15 | const matches = esquery(forLoop, ':not([name="x"])');
16 | assert.equal(18, matches.length);
17 | });
18 |
19 | it('simple function', function () {
20 | const matches = esquery(simpleFunction, ':not(*)');
21 | assert.equal(0, matches.length);
22 | });
23 |
24 | it('simple program', function () {
25 | const matches = esquery(simpleProgram, ':not(Identifier, IfStatement)');
26 | assert.equal(15, matches.length);
27 | });
28 |
29 | it('small program', function () {
30 | const program = {
31 | type: 'Program',
32 | body: [{
33 | type: 'VariableDeclaration',
34 | declarations: [{
35 | type: 'VariableDeclarator',
36 | id: { type: 'Identifier', name: 'x' },
37 | init: { type: 'Literal', value: 1, raw: '1' }
38 | }],
39 | kind: 'var'
40 | }]
41 | };
42 | const matches = esquery(program, ':not([value=1])');
43 |
44 | assert.includeMembers(matches, [
45 | program,
46 | program.body[0],
47 | program.body[0].declarations[0],
48 | program.body[0].declarations[0].id
49 | ]);
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/tests/match.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import forLoop from './fixtures/forLoop.js';
3 | import ast from './fixtures/allClasses.js';
4 |
5 | describe('match', function () {
6 |
7 | it('unknown selector type', function () {
8 | assert.throws(function () {
9 | esquery.match(forLoop, {
10 | type: 'badType'
11 | });
12 | }, Error);
13 | });
14 |
15 | it('unknown selector type', function () {
16 | assert.throws(function () {
17 | esquery.match(forLoop, {
18 | type: 'class',
19 | name: 'badName',
20 | value: { type: 'foobar' } });
21 | }, Error);
22 | });
23 |
24 | it('unknown class name', function () {
25 | assert.throws(function () {
26 | esquery.match(ast, {
27 | type: 'class',
28 | name: 'badName',
29 | value: { type: 'foobar' } });
30 | }, Error);
31 | });
32 |
33 | it('unknown type', function () {
34 | assert.throws(function () {
35 | esquery.match(forLoop, {
36 | type: 'attribute',
37 | name: 'foo',
38 | operator: '=',
39 | value: { type: 'foobar' } });
40 | }, Error);
41 |
42 | assert.throws(function () {
43 | esquery.match(forLoop, {
44 | type: 'attribute',
45 | name: 'foo',
46 | operator: '!=',
47 | value: { type: 'foobar' } });
48 | }, Error);
49 | });
50 |
51 | it('unknown operator', function () {
52 | assert.throws(function () {
53 | esquery.match(forLoop, {
54 | type: 'attribute',
55 | name: 'foo',
56 | operator: 'badOperator',
57 | value: { type: 'foobar' } });
58 | }, Error);
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/tests/queryWildcard.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 | import forLoop from './fixtures/forLoop.js';
4 | import simpleFunction from './fixtures/simpleFunction.js';
5 | import simpleProgram from './fixtures/simpleProgram.js';
6 |
7 | describe('Wildcard query', function () {
8 |
9 | it('empty', function () {
10 | const matches = esquery(conditional, '');
11 | assert.equal(0, matches.length);
12 | });
13 |
14 | it('conditional', function () {
15 | const matches = esquery(conditional, '*');
16 | assert.equal(35, matches.length);
17 | });
18 |
19 | it('for loop', function () {
20 | const matches = esquery(forLoop, '*');
21 | assert.equal(18, matches.length);
22 | });
23 |
24 | it('simple function', function () {
25 | const matches = esquery(simpleFunction, '*');
26 | assert.equal(17, matches.length);
27 | });
28 |
29 | it('simple program', function () {
30 | const matches = esquery(simpleProgram, '*');
31 | assert.equal(22, matches.length);
32 | });
33 |
34 | it('small program', function () {
35 | const program = {
36 | type: 'Program',
37 | body: [{
38 | type: 'VariableDeclaration',
39 | declarations: [{
40 | type: 'VariableDeclarator',
41 | id: { type: 'Identifier', name: 'x' },
42 | init: { type: 'Literal', value: 1, raw: '1' }
43 | }],
44 | kind: 'var'
45 | }]
46 | };
47 | const matches = esquery(program, '*');
48 |
49 | assert.includeMembers(matches, [
50 | program,
51 | program.body[0],
52 | program.body[0].declarations[0],
53 | program.body[0].declarations[0].id,
54 | program.body[0].declarations[0].init
55 | ]);
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { terser } from 'rollup-plugin-terser';
2 |
3 | import nodeResolve from '@rollup/plugin-node-resolve';
4 | import commonjs from '@rollup/plugin-commonjs';
5 | import json from '@rollup/plugin-json';
6 |
7 | import babel from 'rollup-plugin-babel';
8 | import packageJson from './package.json';
9 |
10 | /**
11 | * @external RollupConfig
12 | * @type {PlainObject}
13 | * @see {@link https://rollupjs.org/guide/en#big-list-of-options}
14 | */
15 |
16 | /**
17 | * @param {PlainObject} [config= {}]
18 | * @param {boolean} [config.minifying=false]
19 | * @param {string} [config.format='umd']
20 | * @param {boolean} [config.lite=false]
21 | * @returns {external:RollupConfig}
22 | */
23 | function getRollupObject ({ minifying = false, format = 'umd', lite = false } = {}) {
24 | const nonMinified = {
25 | input: 'esquery.js',
26 | output: {
27 | format,
28 | sourcemap: minifying,
29 | file: [
30 | 'dist/esquery',
31 | lite ? '.lite' : '',
32 | format === 'umd' ? '' : `.${format}`,
33 | minifying ? '.min' : '',
34 | '.js'
35 | ].join(''),
36 | name: 'esquery',
37 | globals: {
38 | estraverse: 'estraverse'
39 | }
40 | },
41 | plugins: [
42 | json(),
43 | nodeResolve(),
44 | commonjs(),
45 | babel()
46 | ]
47 | };
48 | if (lite) {
49 | nonMinified.external = Object.keys(packageJson.dependencies);
50 | }
51 | if (minifying) {
52 | nonMinified.plugins.push(terser());
53 | }
54 | return nonMinified;
55 | }
56 |
57 | export default [
58 | getRollupObject({ minifying: true, format: 'umd' }),
59 | getRollupObject({ minifying: false, format: 'umd' }),
60 | getRollupObject({ minifying: true, format: 'esm' }),
61 | getRollupObject({ minifying: false, format: 'esm' }),
62 | getRollupObject({ minifying: true, format: 'umd', lite: true }),
63 | getRollupObject({ minifying: false, format: 'umd', lite: true })
64 | ];
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ESQuery is a library for querying the AST output by Esprima for patterns of syntax using a CSS style selector system. Check out the demo:
2 |
3 | [demo](https://estools.github.io/esquery/)
4 |
5 | The following selectors are supported:
6 | * AST node type: `ForStatement`
7 | * [wildcard](http://dev.w3.org/csswg/selectors4/#universal-selector): `*`
8 | * [attribute existence](http://dev.w3.org/csswg/selectors4/#attribute-selectors): `[attr]`
9 | * [attribute value](http://dev.w3.org/csswg/selectors4/#attribute-selectors): `[attr="foo"]` or `[attr=123]`
10 | * attribute regex: `[attr=/foo.*/]` or (with flags) `[attr=/foo.*/is]`
11 | * attribute conditions: `[attr!="foo"]`, `[attr>2]`, `[attr<3]`, `[attr>=2]`, or `[attr<=3]`
12 | * nested attribute: `[attr.level2="foo"]`
13 | * field: `FunctionDeclaration > Identifier.id`
14 | * [First](http://dev.w3.org/csswg/selectors4/#the-first-child-pseudo) or [last](http://dev.w3.org/csswg/selectors4/#the-last-child-pseudo) child: `:first-child` or `:last-child`
15 | * [nth-child](http://dev.w3.org/csswg/selectors4/#the-nth-child-pseudo) (no ax+b support): `:nth-child(2)`
16 | * [nth-last-child](http://dev.w3.org/csswg/selectors4/#the-nth-last-child-pseudo) (no ax+b support): `:nth-last-child(1)`
17 | * [descendant](http://dev.w3.org/csswg/selectors4/#descendant-combinators): `ancestor descendant`
18 | * [child](http://dev.w3.org/csswg/selectors4/#child-combinators): `parent > child`
19 | * [following sibling](http://dev.w3.org/csswg/selectors4/#general-sibling-combinators): `node ~ sibling`
20 | * [adjacent sibling](http://dev.w3.org/csswg/selectors4/#adjacent-sibling-combinators): `node + adjacent`
21 | * [negation](http://dev.w3.org/csswg/selectors4/#negation-pseudo): `:not(ForStatement)`
22 | * [has](https://drafts.csswg.org/selectors-4/#has-pseudo): `:has(ForStatement)`, `:has(> ForStatement)`
23 | * [matches-any](http://dev.w3.org/csswg/selectors4/#matches): `:is([attr] > :first-child, :last-child)`
24 | * [subject indicator](http://dev.w3.org/csswg/selectors4/#subject): `!IfStatement > [name="foo"]`
25 | * class of AST node: `:statement`, `:expression`, `:declaration`, `:function`, or `:pattern`
26 |
27 | [](https://travis-ci.org/estools/esquery)
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "esquery",
3 | "version": "1.6.0",
4 | "author": "Joel Feenstra ",
5 | "contributors": [],
6 | "description": "A query library for ECMAScript AST using a CSS selector like query language.",
7 | "main": "dist/esquery.min.js",
8 | "module": "dist/esquery.esm.min.js",
9 | "files": [
10 | "dist/*.js",
11 | "dist/*.map",
12 | "parser.js",
13 | "license.txt",
14 | "README.md"
15 | ],
16 | "nyc": {
17 | "branches": 100,
18 | "lines": 100,
19 | "functions": 100,
20 | "statements": 100,
21 | "reporter": [
22 | "html",
23 | "text"
24 | ],
25 | "exclude": [
26 | "parser.js",
27 | "dist",
28 | "tests"
29 | ]
30 | },
31 | "scripts": {
32 | "prepublishOnly": "npm run build && npm test",
33 | "build:parser": "rm parser.js && pegjs --cache --format umd -o \"parser.js\" \"grammar.pegjs\"",
34 | "build:browser": "rollup -c",
35 | "build": "npm run build:parser && npm run build:browser",
36 | "mocha": "mocha --require chai/register-assert --require @babel/register tests",
37 | "test": "nyc npm run mocha && npm run lint",
38 | "test:ci": "npm run mocha",
39 | "lint": "eslint ."
40 | },
41 | "repository": {
42 | "type": "git",
43 | "url": "https://github.com/estools/esquery.git"
44 | },
45 | "bugs": "https://github.com/estools/esquery/issues",
46 | "homepage": "https://github.com/estools/esquery/",
47 | "keywords": [
48 | "ast",
49 | "ecmascript",
50 | "javascript",
51 | "query"
52 | ],
53 | "devDependencies": {
54 | "@babel/core": "^7.9.0",
55 | "@babel/preset-env": "^7.9.5",
56 | "@babel/register": "^7.9.0",
57 | "@rollup/plugin-commonjs": "^11.1.0",
58 | "@rollup/plugin-json": "^4.0.2",
59 | "@rollup/plugin-node-resolve": "^7.1.3",
60 | "babel-plugin-transform-es2017-object-entries": "0.0.5",
61 | "chai": "4.2.0",
62 | "eslint": "^6.8.0",
63 | "esprima": "~4.0.1",
64 | "mocha": "7.1.1",
65 | "nyc": "^15.0.1",
66 | "pegjs": "~0.10.0",
67 | "rollup": "^1.32.1",
68 | "rollup-plugin-babel": "^4.4.0",
69 | "rollup-plugin-terser": "^5.3.0"
70 | },
71 | "license": "BSD-3-Clause",
72 | "engines": {
73 | "node": ">=0.10"
74 | },
75 | "dependencies": {
76 | "estraverse": "^5.1.0"
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/tests/queryMatches.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 | import forLoop from './fixtures/forLoop.js';
4 | import simpleFunction from './fixtures/simpleFunction.js';
5 | import simpleProgram from './fixtures/simpleProgram.js';
6 |
7 | describe('Pseudo matches query', function () {
8 |
9 | it('conditional matches', function () {
10 | const matches = esquery(conditional, ':matches(IfStatement)');
11 | assert.includeMembers(matches, [
12 | conditional.body[0],
13 | conditional.body[1].alternate
14 | ]);
15 | });
16 |
17 | it('for loop matches', function () {
18 | const matches = esquery(forLoop, ':matches(BinaryExpression, MemberExpression)');
19 | assert.includeMembers(matches, [
20 | forLoop.body[0].test,
21 | forLoop.body[0].body.body[0].expression.callee
22 | ]);
23 | });
24 |
25 | it('simple function matches', function () {
26 | const matches = esquery(simpleFunction, ':matches([name="foo"], ReturnStatement)');
27 | assert.includeMembers(matches, [
28 | simpleFunction.body[0].id,
29 | simpleFunction.body[0].body.body[2]
30 | ]);
31 | });
32 |
33 | it('simple program matches', function () {
34 | const matches = esquery(simpleProgram, ':matches(AssignmentExpression, BinaryExpression)');
35 | assert.includeMembers(matches, [
36 | simpleProgram.body[2].expression,
37 | simpleProgram.body[3].consequent.body[0].expression,
38 | simpleProgram.body[2].expression.right
39 | ]);
40 | });
41 |
42 | it('implicit matches', function () {
43 | const matches = esquery(simpleProgram, 'AssignmentExpression, BinaryExpression, NonExistant');
44 | assert.includeMembers(matches, [
45 | simpleProgram.body[2].expression,
46 | simpleProgram.body[3].consequent.body[0].expression,
47 | simpleProgram.body[2].expression.right
48 | ]);
49 | });
50 |
51 | it('simple function is (alias)', function () {
52 | const matches = esquery(simpleFunction, ':is([name="foo"], ReturnStatement)');
53 | assert.includeMembers(matches, [
54 | simpleFunction.body[0].id,
55 | simpleFunction.body[0].body.body[2]
56 | ]);
57 | });
58 |
59 | });
60 |
--------------------------------------------------------------------------------
/tests/fixtures/allClasses.js:
--------------------------------------------------------------------------------
1 | export default {
2 | type: 'Program',
3 | body: [
4 | {
5 | type: 'FunctionDeclaration',
6 | id: {
7 | type: 'Identifier',
8 | name: 'a'
9 | },
10 | params: [],
11 | defaults: [],
12 | body: {
13 | type: 'BlockStatement',
14 | body: [
15 | {
16 | type: 'ExpressionStatement',
17 | expression: {
18 | type: 'AssignmentExpression',
19 | operator: '=',
20 | left: {
21 | type: 'ArrayPattern',
22 | elements: [
23 | {
24 | type: 'Identifier',
25 | name: 'a'
26 | }
27 | ]
28 | },
29 | right: {
30 | type: 'ArrowFunctionExpression',
31 | params: [],
32 | defaults: [],
33 | rest: null,
34 | body: {
35 | type: 'Literal',
36 | value: 0,
37 | raw: '0'
38 | },
39 | generator: false,
40 | expression: false
41 | }
42 | }
43 | },
44 | {
45 | type: 'ExpressionStatement',
46 | expression: {
47 | type: 'MetaProperty',
48 | meta: {
49 | type: 'Identifier',
50 | name: 'new',
51 | },
52 | property: {
53 | type: 'Identifier',
54 | name: 'target',
55 | },
56 | },
57 | },
58 | {
59 | type: 'ExpressionStatement',
60 | expression: {
61 | type: 'TemplateLiteral',
62 | quasis: [
63 | {
64 | type: 'TemplateElement',
65 | value: {
66 | raw: 'test',
67 | cooked: 'test'
68 | },
69 | tail: true,
70 | }
71 | ],
72 | expressions: [],
73 | },
74 | },
75 | {
76 | type: 'ExpressionStatement',
77 | expression: {
78 | type: 'TemplateLiteral',
79 | quasis: [
80 | {
81 | type: 'TemplateElement',
82 | value: {
83 | raw: 'hello,',
84 | cooked: 'hello,'
85 | },
86 | tail: false,
87 | },
88 | {
89 | type: 'TemplateElement',
90 | value: {
91 | raw: '',
92 | cooked: ''
93 | },
94 | tail: true,
95 | }
96 | ],
97 | expressions: [
98 | {
99 | type: 'Identifier',
100 | name: 'name',
101 | }
102 | ],
103 | },
104 | }
105 | ]
106 | },
107 | rest: null,
108 | generator: false,
109 | expression: false
110 | }
111 | ]
112 | };
113 |
--------------------------------------------------------------------------------
/grammar.pegjs:
--------------------------------------------------------------------------------
1 | {
2 | function nth(n) { return { type: 'nth-child', index: { type: 'literal', value: n } }; }
3 | function nthLast(n) { return { type: 'nth-last-child', index: { type: 'literal', value: n } }; }
4 | function strUnescape(s) {
5 | return s.replace(/\\(.)/g, function(match, ch) {
6 | switch(ch) {
7 | case 'b': return '\b';
8 | case 'f': return '\f';
9 | case 'n': return '\n';
10 | case 'r': return '\r';
11 | case 't': return '\t';
12 | case 'v': return '\v';
13 | default: return ch;
14 | }
15 | });
16 | }
17 | }
18 |
19 | start
20 | = _ ss:selectors _ {
21 | return ss.length === 1 ? ss[0] : { type: 'matches', selectors: ss };
22 | }
23 | / _ { return void 0; }
24 |
25 | _ = " "*
26 | identifierName = i:[^ [\],():#!=><~+.]+ { return i.join(''); }
27 | binaryOp
28 | = _ ">" _ { return 'child'; }
29 | / _ "~" _ { return 'sibling'; }
30 | / _ "+" _ { return 'adjacent'; }
31 | / " " _ { return 'descendant'; }
32 |
33 | hasSelectors = s:hasSelector ss:(_ "," _ hasSelector)* {
34 | return [s].concat(ss.map(function (s) { return s[3]; }));
35 | }
36 |
37 | selectors = s:selector ss:(_ "," _ selector)* {
38 | return [s].concat(ss.map(function (s) { return s[3]; }));
39 | }
40 |
41 |
42 | hasSelector
43 | = op:binaryOp? s:selector {
44 | if (!op) return s;
45 | return { type: op, left: { type: 'exactNode' }, right: s };
46 | }
47 |
48 | selector
49 | = a:sequence ops:(binaryOp sequence)* {
50 | return ops.reduce(function (memo, rhs) {
51 | return { type: rhs[0], left: memo, right: rhs[1] };
52 | }, a);
53 | }
54 |
55 | sequence
56 | = subject:"!"? as:atom+ {
57 | const b = as.length === 1 ? as[0] : { type: 'compound', selectors: as };
58 | if(subject) b.subject = true;
59 | return b;
60 | }
61 |
62 | atom
63 | = wildcard / identifier / attr / field / negation / matches / is
64 | / has / firstChild / lastChild / nthChild / nthLastChild / class
65 |
66 | wildcard = a:"*" { return { type: 'wildcard', value: a }; }
67 | identifier = "#"? i:identifierName { return { type: 'identifier', value: i }; }
68 |
69 | attr
70 | = "[" _ v:attrValue _ "]" { return v; }
71 | attrOps = a:[><]
72 | attrEqOps = a:"!"? "=" { return (a || '') + '='; }
73 | attrName = a:identifierName as:("." identifierName)* {
74 | return [].concat.apply([a], as).join('');
75 | }
76 | attrValue
77 | = name:attrName _ op:attrEqOps _ value:(type / regex) {
78 | return { type: 'attribute', name: name, operator: op, value: value };
79 | }
80 | / name:attrName _ op:attrOps _ value:(string / number / path) {
81 | return { type: 'attribute', name: name, operator: op, value: value };
82 | }
83 | / name:attrName { return { type: 'attribute', name: name }; }
84 | string
85 | = "\"" d:([^\\"] / a:"\\" b:. { return a + b; })* "\"" {
86 | return { type: 'literal', value: strUnescape(d.join('')) };
87 | }
88 | / "'" d:([^\\'] / a:"\\" b:. { return a + b; })* "'" {
89 | return { type: 'literal', value: strUnescape(d.join('')) };
90 | }
91 | number
92 | = a:([0-9]* ".")? b:[0-9]+ {
93 | // Can use `a.flat().join('')` once supported
94 | const leadingDecimals = a ? [].concat.apply([], a).join('') : '';
95 | return { type: 'literal', value: parseFloat(leadingDecimals + b.join('')) };
96 | }
97 | path = i:identifierName { return { type: 'literal', value: i }; }
98 | type = "type(" _ t:[^ )]+ _ ")" { return { type: 'type', value: t.join('') }; }
99 | flags = [imsu]+
100 | regex = "/" d:[^/]+ "/" flgs:flags? { return {
101 | type: 'regexp', value: new RegExp(d.join(''), flgs ? flgs.join('') : '') };
102 | }
103 |
104 | field = "." i:identifierName is:("." identifierName)* {
105 | return { type: 'field', name: is.reduce(function(memo, p){ return memo + p[0] + p[1]; }, i)};
106 | }
107 |
108 | negation = ":not(" _ ss:selectors _ ")" { return { type: 'not', selectors: ss }; }
109 | matches = ":matches(" _ ss:selectors _ ")" { return { type: 'matches', selectors: ss }; }
110 | is = ":is(" _ ss:selectors _ ")" { return { type: 'matches', selectors: ss }; }
111 | has = ":has(" _ ss:hasSelectors _ ")" { return { type: 'has', selectors: ss }; }
112 |
113 | firstChild = ":first-child" { return nth(1); }
114 | lastChild = ":last-child" { return nthLast(1); }
115 | nthChild = ":nth-child(" _ n:[0-9]+ _ ")" { return nth(parseInt(n.join(''), 10)); }
116 | nthLastChild = ":nth-last-child(" _ n:[0-9]+ _ ")" { return nthLast(parseInt(n.join(''), 10)); }
117 |
118 |
119 | class = ":" c:identifierName {
120 | return { type: 'class', name: c };
121 | }
122 |
--------------------------------------------------------------------------------
/tests/queryType.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 | import forLoop from './fixtures/forLoop.js';
4 | import simpleFunction from './fixtures/simpleFunction.js';
5 | import simpleProgram from './fixtures/simpleProgram.js';
6 |
7 | describe('Type query', function () {
8 |
9 | it('conditional', function () {
10 | let matches = esquery(conditional, 'Program');
11 | assert.includeMembers(matches, [conditional]);
12 |
13 | matches = esquery(conditional, 'IfStatement');
14 | assert.includeMembers(matches, [
15 | conditional.body[0],
16 | conditional.body[1],
17 | conditional.body[1].alternate
18 | ]);
19 |
20 | matches = esquery(conditional, 'LogicalExpression');
21 | assert.includeMembers(matches, [
22 | conditional.body[1].test,
23 | conditional.body[1].test.left
24 | ]);
25 |
26 | matches = esquery(conditional, 'ExpressionStatement');
27 | assert.includeMembers(matches, [
28 | conditional.body[0].consequent.body[0],
29 | conditional.body[0].alternate.body[0],
30 | conditional.body[1].consequent.body[0],
31 | conditional.body[1].alternate.consequent.body[0]
32 | ]);
33 | });
34 |
35 | it('for loop', function () {
36 | let matches = esquery(forLoop, 'Program');
37 | assert.includeMembers(matches, [forLoop]);
38 |
39 | matches = esquery(forLoop, 'ForStatement');
40 | assert.includeMembers(matches, [
41 | forLoop.body[0]
42 | ]);
43 |
44 | matches = esquery(forLoop, 'BinaryExpression');
45 | assert.includeMembers(matches, [
46 | forLoop.body[0].test
47 | ]);
48 | });
49 |
50 | it('simple function', function () {
51 | let matches = esquery(simpleFunction, 'Program');
52 | assert.includeMembers(matches, [simpleFunction]);
53 |
54 | matches = esquery(simpleFunction, 'VariableDeclaration');
55 | assert.includeMembers(matches, [
56 | simpleFunction.body[0].body.body[0]
57 | ]);
58 |
59 | matches = esquery(simpleFunction, 'FunctionDeclaration');
60 | assert.includeMembers(matches, [
61 | simpleFunction.body[0]
62 | ]);
63 |
64 | matches = esquery(simpleFunction, 'ReturnStatement');
65 | assert.includeMembers(matches, [
66 | simpleFunction.body[0].body.body[2]
67 | ]);
68 | });
69 |
70 | it('simple program', function () {
71 | let matches = esquery(simpleProgram, 'Program');
72 | assert.includeMembers(matches, [simpleProgram]);
73 |
74 | matches = esquery(simpleProgram, 'VariableDeclaration');
75 | assert.includeMembers(matches, [
76 | simpleProgram.body[0],
77 | simpleProgram.body[1]
78 | ]);
79 |
80 | matches = esquery(simpleProgram, 'AssignmentExpression');
81 | assert.includeMembers(matches, [
82 | simpleProgram.body[2].expression,
83 | simpleProgram.body[3].consequent.body[0].expression
84 | ]);
85 |
86 | matches = esquery(simpleProgram, 'Identifier');
87 | assert.includeMembers(matches, [
88 | simpleProgram.body[0].declarations[0].id,
89 | simpleProgram.body[1].declarations[0].id,
90 | simpleProgram.body[2].expression.left,
91 | simpleProgram.body[2].expression.right.left,
92 | simpleProgram.body[3].test,
93 | simpleProgram.body[3].consequent.body[0].expression.left
94 | ]);
95 | });
96 |
97 | it('# type', function () {
98 | let matches = esquery(forLoop, '#Program');
99 | assert.includeMembers(matches, [
100 | forLoop
101 | ]);
102 |
103 | matches = esquery(forLoop, '#ForStatement');
104 | assert.includeMembers(matches, [
105 | forLoop.body[0]
106 | ]);
107 |
108 | matches = esquery(forLoop, '#BinaryExpression');
109 | assert.includeMembers(matches, [
110 | forLoop.body[0].test
111 | ]);
112 | });
113 |
114 | it('case insensitive type', function () {
115 | let matches = esquery(forLoop, 'Program');
116 | assert.includeMembers(matches, [
117 | forLoop
118 | ]);
119 |
120 | matches = esquery(forLoop, 'forStatement');
121 | assert.includeMembers(matches, [
122 | forLoop.body[0]
123 | ]);
124 |
125 | matches = esquery(forLoop, 'binaryexpression');
126 | assert.includeMembers(matches, [
127 | forLoop.body[0].test
128 | ]);
129 | });
130 | });
131 |
--------------------------------------------------------------------------------
/tests/queryClass.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import ast from './fixtures/allClasses.js';
3 | import customNodesWithKind from './fixtures/customNodesWithKind.js';
4 |
5 | describe('Class query', function () {
6 |
7 | it(':statement', function () {
8 | const matches = esquery(ast, ':statement');
9 | assert.includeMembers(matches, [
10 | ast.body[0],
11 | ast.body[0].body,
12 | ast.body[0].body.body[0],
13 | ast.body[0].body.body[1],
14 | ast.body[0].body.body[2],
15 | ast.body[0].body.body[3]
16 | ]);
17 | assert.equal(6, matches.length);
18 | });
19 |
20 | it(':expression', function () {
21 | const matches = esquery(ast, ':Expression');
22 | assert.includeMembers(matches, [
23 | ast.body[0].id,
24 | ast.body[0].body.body[0].expression,
25 | ast.body[0].body.body[0].expression.left.elements[0],
26 | ast.body[0].body.body[0].expression.right,
27 | ast.body[0].body.body[0].expression.right.body,
28 | ast.body[0].body.body[1].expression,
29 | ast.body[0].body.body[2].expression,
30 | ast.body[0].body.body[3].expression,
31 | ast.body[0].body.body[3].expression.expressions[0]
32 | ]);
33 | assert.equal(9, matches.length);
34 | });
35 |
36 | it(':function', function () {
37 | const matches = esquery(ast, ':FUNCTION');
38 | assert.includeMembers(matches, [
39 | ast.body[0],
40 | ast.body[0].body.body[0].expression.right
41 | ]);
42 | assert.equal(2, matches.length);
43 | });
44 |
45 | it(':declaration', function () {
46 | const matches = esquery(ast, ':declaratioN');
47 | assert.includeMembers(matches, [
48 | ast.body[0]
49 | ]);
50 | assert.equal(1, matches.length);
51 | });
52 |
53 | it(':pattern', function () {
54 | const matches = esquery(ast, ':paTTern');
55 | assert.includeMembers(matches, [
56 | ast.body[0].id,
57 | ast.body[0].body.body[0].expression,
58 | ast.body[0].body.body[0].expression.left,
59 | ast.body[0].body.body[0].expression.left.elements[0],
60 | ast.body[0].body.body[0].expression.right,
61 | ast.body[0].body.body[0].expression.right.body,
62 | ast.body[0].body.body[1].expression,
63 | ast.body[0].body.body[2].expression,
64 | ast.body[0].body.body[3].expression,
65 | ast.body[0].body.body[3].expression.expressions[0]
66 | ]);
67 | assert.equal(10, matches.length);
68 | });
69 |
70 | it(':expression as custom matcher', function () {
71 | const matches = esquery(ast, ':expression', {
72 | matchClass(className, node, ancestry) {
73 | if (className !== 'expression') return false;
74 |
75 | return node.type.slice(-10) === 'Expression' ||
76 | node.type.slice(-7) === 'Literal' ||
77 | (
78 | node.type === 'Identifier' &&
79 | (ancestry.length === 0 || ancestry[0].type !== 'MetaProperty')
80 | ) ||
81 | node.type === 'MetaProperty';
82 |
83 | }
84 | });
85 |
86 | assert.includeMembers(matches, [
87 | ast.body[0].id,
88 | ast.body[0].body.body[0].expression,
89 | ast.body[0].body.body[0].expression.left.elements[0],
90 | ast.body[0].body.body[0].expression.right,
91 | ast.body[0].body.body[0].expression.right.body,
92 | ast.body[0].body.body[1].expression,
93 | ast.body[0].body.body[2].expression,
94 | ast.body[0].body.body[3].expression,
95 | ast.body[0].body.body[3].expression.expressions[0]
96 | ]);
97 | assert.equal(9, matches.length);
98 | });
99 |
100 | it('custom nodes with :expression, :statement', function () {
101 | const options = {
102 | visitorKeys: {
103 | CustomRoot: ['list'],
104 | CustomChild: ['sublist'],
105 | CustomGrandChild: [],
106 | CustomStatement: [],
107 | CustomExpression: []
108 | },
109 | nodeTypeKey: 'kind'
110 | };
111 |
112 | const matches1 = esquery(customNodesWithKind, ':expression', options);
113 | assert.equal(0, matches1.length);
114 |
115 | const matches2 = esquery(customNodesWithKind, ':statement', options);
116 | assert.equal(0, matches2.length);
117 | });
118 |
119 | it('custom nodes with custom class matcher', function () {
120 | const options = {
121 | visitorKeys: {
122 | CustomRoot: ['list'],
123 | CustomChild: ['sublist'],
124 | CustomGrandChild: [],
125 | CustomStatement: [],
126 | CustomExpression: []
127 | },
128 | nodeTypeKey: 'kind',
129 | matchClass(className, node) {
130 | return className === 'root' && node.kind === 'CustomRoot';
131 | }
132 | };
133 |
134 | const matches = esquery(customNodesWithKind, ':root', options);
135 | assert.equal(1, matches.length);
136 | assert.strictEqual(customNodesWithKind, matches[0]);
137 | });
138 |
139 | });
140 |
--------------------------------------------------------------------------------
/tests/querySubject.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 | import forLoop from './fixtures/forLoop.js';
4 | import simpleFunction from './fixtures/simpleFunction.js';
5 | import simpleProgram from './fixtures/simpleProgram.js';
6 |
7 | import nestedFunctions from './fixtures/nestedFunctions.js';
8 | import bigArray from './fixtures/bigArray.js';
9 | import customNodes from './fixtures/customNodes.js';
10 |
11 | describe('Query subject', function () {
12 |
13 | it('type subject', function () {
14 | const matches = esquery(conditional, '!IfStatement Identifier');
15 | assert.includeMembers(matches, [
16 | conditional.body[0],
17 | conditional.body[1],
18 | conditional.body[1].alternate
19 | ]);
20 | });
21 |
22 | it('* subject', function () {
23 | const matches = esquery(forLoop, '!* > [name="foo"]');
24 | assert.includeMembers(matches, [
25 | forLoop.body[0].test.right,
26 | forLoop.body[0].body.body[0].expression.callee
27 | ]);
28 | });
29 |
30 | it(':nth-child subject', function () {
31 | const matches = esquery(simpleFunction, '!:nth-child(1) [name="y"]');
32 | assert.includeMembers(matches, [
33 | simpleFunction.body[0],
34 | simpleFunction.body[0].body.body[0],
35 | simpleFunction.body[0].body.body[0].declarations[0]
36 | ]);
37 | });
38 |
39 | it(':nth-last-child subject', function () {
40 | const matches = esquery(simpleProgram, '!:nth-last-child(1) [name="y"]');
41 | assert.includeMembers(matches, [
42 | simpleProgram.body[3],
43 | simpleProgram.body[1].declarations[0],
44 | simpleProgram.body[3].consequent.body[0]
45 | ]);
46 | });
47 |
48 | it('attribute literal subject', function () {
49 | const matches = esquery(simpleProgram, '![test] [name="y"]');
50 | assert.includeMembers(matches, [
51 | simpleProgram.body[3]
52 | ]);
53 | });
54 |
55 | it('attribute type subject', function () {
56 | const matches = esquery(nestedFunctions, '![generator=type(boolean)] > BlockStatement');
57 | assert.includeMembers(matches, [
58 | nestedFunctions.body[0],
59 | nestedFunctions.body[0].body.body[1]
60 | ]);
61 | });
62 |
63 | it('attribute regexp subject', function () {
64 | const matches = esquery(conditional, '![operator=/=+/] > [name="x"]');
65 | assert.includeMembers(matches, [
66 | conditional.body[0].test,
67 | conditional.body[0].alternate.body[0].expression,
68 | conditional.body[1].test.left.left
69 | ]);
70 | });
71 |
72 | it('field subject', function () {
73 | const matches = esquery(forLoop, '!.test');
74 | assert.includeMembers(matches, [
75 | forLoop.body[0].test
76 | ]);
77 | });
78 |
79 | it(':matches subject', function () {
80 | const matches = esquery(forLoop, '!:matches(*) > [name="foo"]');
81 | assert.includeMembers(matches, [
82 | forLoop.body[0].test.right,
83 | forLoop.body[0].body.body[0].expression.callee
84 | ]);
85 | });
86 |
87 | it(':not subject', function () {
88 | const matches = esquery(nestedFunctions, '!:not(BlockStatement) > [name="foo"]');
89 | assert.includeMembers(matches, [
90 | nestedFunctions.body[0]
91 | ]);
92 | });
93 |
94 | it('compound attributes subject', function () {
95 | const matches = esquery(conditional, '![left.name="x"][right.value=1]');
96 | assert.includeMembers(matches, [
97 | conditional.body[0].test
98 | ]);
99 | });
100 |
101 | it('descendant right subject', function () {
102 | const matches = esquery(forLoop, '* !AssignmentExpression');
103 | assert.includeMembers(matches, [
104 | forLoop.body[0].init
105 | ]);
106 | });
107 |
108 | it('child right subject', function () {
109 | const matches = esquery(forLoop, '* > !AssignmentExpression');
110 | assert.includeMembers(matches, [
111 | forLoop.body[0].init
112 | ]);
113 | });
114 |
115 | it('sibling left subject', function () {
116 | const matches = esquery(simpleProgram, '!VariableDeclaration ~ IfStatement');
117 | assert.includeMembers(matches, [
118 | simpleProgram.body[0],
119 | simpleProgram.body[1]
120 | ]);
121 | });
122 |
123 | it('sibling right subject', function () {
124 | const matches = esquery(simpleProgram, '!VariableDeclaration ~ !IfStatement');
125 | assert.includeMembers(matches, [
126 | simpleProgram.body[0],
127 | simpleProgram.body[1],
128 | simpleProgram.body[3]
129 | ]);
130 | });
131 |
132 | it('adjacent right subject', function () {
133 | const matches = esquery(simpleProgram, '!VariableDeclaration + !ExpressionStatement');
134 | assert.includeMembers(matches, [
135 | simpleProgram.body[1],
136 | simpleProgram.body[2]
137 | ]);
138 | });
139 |
140 | it('multiple adjacent siblings', function () {
141 | const matches = esquery(bigArray, 'Identifier + Identifier');
142 | assert.includeMembers(matches, [
143 | bigArray.body[0].expression.elements[4],
144 | bigArray.body[0].expression.elements[8]
145 | ]);
146 | assert.equal(2, matches.length);
147 | });
148 |
149 | it('multiple siblings', function () {
150 | const matches = esquery(bigArray, 'Identifier ~ Identifier');
151 | assert.includeMembers(matches, [
152 | bigArray.body[0].expression.elements[4],
153 | bigArray.body[0].expression.elements[7],
154 | bigArray.body[0].expression.elements[8]
155 | ]);
156 | assert.equal(3, matches.length);
157 | });
158 | });
159 |
160 | describe('Query subject with custom ast', function () {
161 | const visitorKeys = {
162 | CustomRoot: ['list'],
163 | CustomChild: ['sublist'],
164 | CustomGrandChild: []
165 | };
166 |
167 | it('sibling', function () {
168 | const matches = esquery(customNodes, 'CustomChild[name=two] ~ CustomChild', { visitorKeys });
169 | assert.includeMembers(matches, [
170 | customNodes.list[2],
171 | customNodes.list[3],
172 | ]);
173 | });
174 |
175 |
176 | it('sibling with fallback', function () {
177 | const matches = esquery(customNodes, 'CustomChild[name=two] ~ CustomChild', {
178 | fallback (node) {
179 | return node.type === 'CustomRoot' ? ['list'] : node.type === 'CustomChild' ? ['sublist'] : [];
180 | }
181 | });
182 | assert.includeMembers(matches, [
183 | customNodes.list[2],
184 | customNodes.list[3],
185 | ]);
186 | });
187 |
188 | it('sibling with default fallback', function () {
189 | const matches = esquery(customNodes, 'CustomChild[name=two] ~ CustomChild');
190 | assert.includeMembers(matches, [
191 | customNodes.list[2],
192 | customNodes.list[3],
193 | ]);
194 | });
195 |
196 | it('adjacent', function () {
197 | const matches = esquery(customNodes, 'CustomChild[name=two] + CustomChild', { visitorKeys });
198 | assert.includeMembers(matches, [
199 | customNodes.list[2],
200 | ]);
201 | });
202 | });
203 |
--------------------------------------------------------------------------------
/tests/matches.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import forLoop from './fixtures/forLoop.js';
3 | import simpleProgram from './fixtures/simpleProgram.js';
4 | import conditional from './fixtures/conditional.js';
5 | import customNodes from './fixtures/customNodes.js';
6 | import customNodesWithKind from './fixtures/customNodesWithKind.js';
7 |
8 | describe('matches', function () {
9 | it('falsey node', function () {
10 | const selector = esquery.parse('*');
11 |
12 | assert.equal(false, esquery.matches(
13 | null,
14 | selector,
15 | []
16 | ));
17 |
18 | assert.equal(false, esquery.matches(
19 | '',
20 | selector,
21 | []
22 | ));
23 |
24 | assert.equal(false, esquery.matches(
25 | false,
26 | selector,
27 | []
28 | ));
29 | });
30 |
31 | it('falsey selector', function () {
32 | assert.equal(true, esquery.matches(
33 | forLoop,
34 | null,
35 | []
36 | ));
37 |
38 | assert.equal(true, esquery.matches(
39 | forLoop,
40 | '',
41 | []
42 | ));
43 |
44 | assert.equal(true, esquery.matches(
45 | forLoop,
46 | false,
47 | []
48 | ));
49 | });
50 |
51 | it('falsey ancestry', function () {
52 | const selector = esquery.parse('*');
53 |
54 | assert.doesNotThrow(() => {
55 | esquery.matches(
56 | forLoop,
57 | selector,
58 | null
59 | );
60 | });
61 |
62 | assert.doesNotThrow(() => {
63 | esquery.matches(
64 | forLoop,
65 | selector,
66 | ''
67 | );
68 | });
69 |
70 | assert.doesNotThrow(() => {
71 | esquery.matches(
72 | forLoop,
73 | selector,
74 | false
75 | );
76 | });
77 | });
78 |
79 | it('missing parent', function () {
80 | let selector = esquery.parse('!VariableDeclaration + !ExpressionStatement');
81 | assert.doesNotThrow(() => {
82 | esquery.matches(
83 | simpleProgram.body[2],
84 | selector,
85 | []
86 | );
87 | });
88 |
89 | selector = esquery.parse('!VariableDeclaration ~ IfStatement');
90 | assert.doesNotThrow(() => {
91 | esquery.matches(
92 | simpleProgram.body[3],
93 | selector,
94 | []
95 | );
96 | });
97 | });
98 |
99 | it('adjacent/sibling', function () {
100 | let selector = esquery.parse('!VariableDeclaration + !ExpressionStatement');
101 | assert.doesNotThrow(() => {
102 | esquery.matches(
103 | simpleProgram.body[2],
104 | selector,
105 | simpleProgram.body
106 | );
107 | });
108 |
109 | selector = esquery.parse('!VariableDeclaration ~ IfStatement');
110 | assert.doesNotThrow(() => {
111 | esquery.matches(
112 | simpleProgram.body[3],
113 | selector,
114 | simpleProgram.body
115 | );
116 | });
117 | });
118 |
119 | it('Non-array list prop', function () {
120 | let selector = esquery.parse('!IfStatement ~ IfStatement');
121 | assert.doesNotThrow(() => {
122 | esquery.matches(
123 | conditional.body[1],
124 | selector,
125 | conditional.body
126 | );
127 | });
128 |
129 | selector = esquery.parse('!IfStatement + IfStatement');
130 | assert.doesNotThrow(() => {
131 | esquery.matches(
132 | conditional.body[1],
133 | selector,
134 | conditional.body
135 | );
136 | });
137 | });
138 | });
139 |
140 | describe('matches with custom AST and custom visitor keys', function () {
141 | it('adjacent/sibling', function () {
142 | const options = {
143 | visitorKeys: {
144 | CustomRoot: ['list'],
145 | CustomChild: ['sublist'],
146 | CustomGrandChild: []
147 | }
148 | };
149 | let selector = esquery.parse('CustomChild + CustomChild');
150 | assert.doesNotThrow(() => {
151 | esquery.matches(
152 | customNodes.list[1],
153 | selector,
154 | [customNodes],
155 | options
156 | );
157 | });
158 |
159 | selector = esquery.parse('CustomChild ~ CustomChild');
160 | assert.doesNotThrow(() => {
161 | esquery.matches(
162 | customNodes.list[1],
163 | selector,
164 | [customNodes],
165 | options
166 | );
167 | });
168 | });
169 | });
170 |
171 | describe('matches with custom AST and nodeTypeKey and custom visitor keys', function () {
172 | it('adjacent/sibling', function () {
173 | const options = {
174 | visitorKeys: {
175 | CustomRoot: ['list'],
176 | CustomChild: ['sublist'],
177 | CustomGrandChild: [],
178 | CustomStatement: [],
179 | CustomExpression: []
180 | },
181 | nodeTypeKey: 'kind'
182 | };
183 |
184 | let selector = esquery.parse('CustomChild + CustomChild');
185 | assert.doesNotThrow(() => {
186 | esquery.matches(
187 | customNodesWithKind.list[1],
188 | selector,
189 | [customNodesWithKind],
190 | options
191 | );
192 | });
193 |
194 | selector = esquery.parse('CustomChild ~ CustomChild');
195 | assert.doesNotThrow(() => {
196 | esquery.matches(
197 | customNodesWithKind.list[1],
198 | selector,
199 | [customNodesWithKind],
200 | options
201 | );
202 | });
203 | });
204 | });
205 |
206 | describe('matches with custom AST and fallback option', function () {
207 | it('adjacent/sibling', function () {
208 | const options = {
209 | fallback (node) {
210 | return node.type === 'CustomRoot' ? ['list'] : node.type === 'CustomChild' ? ['sublist'] : [];
211 | }
212 | };
213 | let selector = esquery.parse('CustomChild + CustomChild');
214 | assert.doesNotThrow(() => {
215 | esquery.matches(
216 | customNodes.list[1],
217 | selector,
218 | [customNodes],
219 | options
220 | );
221 | });
222 |
223 | selector = esquery.parse('CustomChild ~ CustomChild');
224 | assert.doesNotThrow(() => {
225 | esquery.matches(
226 | customNodes.list[1],
227 | selector,
228 | [customNodes],
229 | options
230 | );
231 | });
232 | });
233 | });
234 |
235 | describe('matches with custom AST and default fallback', function () {
236 | it('adjacent/sibling', function () {
237 | let selector = esquery.parse('CustomChild + CustomChild');
238 | assert.doesNotThrow(() => {
239 | esquery.matches(
240 | customNodes.list[1],
241 | selector,
242 | [customNodes],
243 | );
244 | });
245 |
246 | selector = esquery.parse('CustomChild ~ CustomChild');
247 | assert.doesNotThrow(() => {
248 | esquery.matches(
249 | customNodes.list[1],
250 | selector,
251 | [customNodes],
252 | );
253 | });
254 | });
255 | });
256 |
--------------------------------------------------------------------------------
/tests/queryPseudoChild.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import conditional from './fixtures/conditional.js';
3 | import conditionalLong from './fixtures/conditionalLong.js';
4 | import forLoop from './fixtures/forLoop.js';
5 | import simpleFunction from './fixtures/simpleFunction.js';
6 | import simpleProgram from './fixtures/simpleProgram.js';
7 | import customNodes from './fixtures/customNodes.js';
8 |
9 | describe('Pseudo *-child query', function () {
10 |
11 | it('conditional first child', function () {
12 | const matches = esquery(conditional, ':first-child');
13 | assert.includeMembers(matches, [
14 | conditional.body[0],
15 | conditional.body[0].consequent.body[0],
16 | conditional.body[0].alternate.body[0],
17 | conditional.body[1].consequent.body[0],
18 | conditional.body[1].alternate.consequent.body[0]
19 | ]);
20 | });
21 |
22 | it('conditional last child', function () {
23 | const matches = esquery(conditional, ':last-child');
24 | assert.includeMembers(matches, [
25 | conditional.body[1],
26 | conditional.body[0].consequent.body[0],
27 | conditional.body[0].alternate.body[0],
28 | conditional.body[1].consequent.body[0],
29 | conditional.body[1].alternate.consequent.body[0]
30 | ]);
31 | });
32 |
33 | it('conditional nth child', function () {
34 | let matches = esquery(conditional, ':nth-child(2)');
35 | assert.includeMembers(matches, [
36 | conditional.body[1]
37 | ]);
38 |
39 | matches = esquery(conditional, ':nth-last-child(2)');
40 | assert.includeMembers(matches, [
41 | conditional.body[0]
42 | ]);
43 | });
44 |
45 | it('conditional nth child (multiple digits)', function () {
46 | const matches = esquery(conditionalLong, ':nth-child(10)');
47 | assert.includeMembers(matches, [
48 | conditionalLong.body[9]
49 | ]);
50 | });
51 |
52 | it('conditional nth-last child (multiple digits)', function () {
53 | const matches = esquery(conditionalLong, ':nth-last-child(10)');
54 | assert.includeMembers(matches, [
55 | conditionalLong.body[1]
56 | ]);
57 | });
58 |
59 | it('for loop first child', function () {
60 | const matches = esquery(forLoop, ':first-child');
61 | assert.includeMembers(matches, [
62 | forLoop.body[0],
63 | forLoop.body[0].body.body[0]
64 | ]);
65 | });
66 |
67 | it('for loop last child', function () {
68 | const matches = esquery(forLoop, ':last-child');
69 | assert.includeMembers(matches, [
70 | forLoop.body[0],
71 | forLoop.body[0].body.body[0]
72 | ]);
73 | });
74 |
75 | it('for loop nth child', function () {
76 | const matches = esquery(forLoop, ':nth-last-child(1)');
77 | assert.includeMembers(matches, [
78 | forLoop.body[0],
79 | forLoop.body[0].body.body[0]
80 | ]);
81 | });
82 |
83 | it('simple function first child', function () {
84 | const matches = esquery(simpleFunction, ':first-child');
85 | assert.includeMembers(matches, [
86 | simpleFunction.body[0],
87 | simpleFunction.body[0].params[0],
88 | simpleFunction.body[0].body.body[0],
89 | simpleFunction.body[0].body.body[0].declarations[0]
90 | ]);
91 | });
92 |
93 | it('simple function last child', function () {
94 | const matches = esquery(simpleFunction, ':last-child');
95 | assert.includeMembers(matches, [
96 | simpleFunction.body[0],
97 | simpleFunction.body[0].params[1],
98 | simpleFunction.body[0].body.body[2],
99 | simpleFunction.body[0].body.body[0].declarations[0]
100 | ]);
101 | });
102 |
103 | it('simple function nth child', function () {
104 | let matches = esquery(simpleFunction, ':nth-child(2)');
105 | assert.includeMembers(matches, [
106 | simpleFunction.body[0].params[1],
107 | simpleFunction.body[0].body.body[1]
108 | ]);
109 |
110 | matches = esquery(simpleFunction, ':nth-child(3)');
111 | assert.includeMembers(matches, [
112 | simpleFunction.body[0].body.body[2]
113 | ]);
114 |
115 | matches = esquery(simpleFunction, ':nth-last-child(2)');
116 | assert.includeMembers(matches, [
117 | simpleFunction.body[0].params[0],
118 | simpleFunction.body[0].body.body[1]
119 | ]);
120 | });
121 |
122 | it('simple program first child', function () {
123 | const matches = esquery(simpleProgram, ':first-child');
124 | assert.includeMembers(matches, [
125 | simpleProgram.body[0],
126 | simpleProgram.body[0].declarations[0],
127 | simpleProgram.body[1].declarations[0],
128 | simpleProgram.body[3].consequent.body[0]
129 | ]);
130 | });
131 |
132 | it('simple program last child', function () {
133 | const matches = esquery(simpleProgram, ':last-child');
134 | assert.includeMembers(matches, [
135 | simpleProgram.body[3],
136 | simpleProgram.body[0].declarations[0],
137 | simpleProgram.body[1].declarations[0],
138 | simpleProgram.body[3].consequent.body[0]
139 | ]);
140 | });
141 |
142 | it('simple program nth child', function () {
143 | let matches = esquery(simpleProgram, ':nth-child(2)');
144 | assert.includeMembers(matches, [
145 | simpleProgram.body[1]
146 | ]);
147 |
148 | matches = esquery(simpleProgram, ':nth-child(3)');
149 | assert.includeMembers(matches, [
150 | simpleProgram.body[2]
151 | ]);
152 |
153 | matches = esquery(simpleProgram, ':nth-last-child(2)');
154 | assert.includeMembers(matches, [
155 | simpleProgram.body[2]
156 | ]);
157 | });
158 | });
159 |
160 | describe('Pseudo *-child query with custom ast', function () {
161 | const visitorKeys = {
162 | CustomRoot: ['list'],
163 | CustomChild: ['sublist'],
164 | CustomGrandChild: []
165 | };
166 |
167 | it('conditional first child', function () {
168 | const matches = esquery(customNodes, ':first-child', { visitorKeys });
169 | assert.includeMembers(matches, [
170 | customNodes.list[0],
171 | customNodes.list[0].sublist[0],
172 | customNodes.list[2].sublist[0],
173 | customNodes.list[3].sublist[0],
174 | ]);
175 | });
176 |
177 | it('conditional first child with fallback', function () {
178 | const matches = esquery(customNodes, ':first-child', {
179 | fallback (node) {
180 | return node.type === 'CustomRoot' ? ['list'] : node.type === 'CustomChild' ? ['sublist'] : [];
181 | }
182 | });
183 | assert.includeMembers(matches, [
184 | customNodes.list[0],
185 | customNodes.list[0].sublist[0],
186 | customNodes.list[2].sublist[0],
187 | customNodes.list[3].sublist[0],
188 | ]);
189 | });
190 |
191 | it('conditional first child with default fallback', function () {
192 | const matches = esquery(customNodes, ':first-child');
193 | assert.includeMembers(matches, [
194 | customNodes.list[0],
195 | customNodes.list[0].sublist[0],
196 | customNodes.list[2].sublist[0],
197 | customNodes.list[3].sublist[0],
198 | ]);
199 | });
200 |
201 | it('conditional last child', function () {
202 | const matches = esquery(customNodes, ':last-child', { visitorKeys });
203 | assert.includeMembers(matches, [
204 | customNodes.list[3],
205 | customNodes.list[0].sublist[0],
206 | customNodes.list[2].sublist[1],
207 | customNodes.list[3].sublist[2],
208 | ]);
209 | });
210 |
211 | it('conditional nth child', function () {
212 | let matches = esquery(customNodes, ':nth-child(2)', { visitorKeys });
213 | assert.includeMembers(matches, [
214 | customNodes.list[1],
215 | customNodes.list[2].sublist[1],
216 | customNodes.list[3].sublist[1],
217 | ]);
218 |
219 | matches = esquery(customNodes, ':nth-last-child(2)', { visitorKeys });
220 | assert.includeMembers(matches, [
221 | customNodes.list[2],
222 | customNodes.list[2].sublist[0],
223 | customNodes.list[3].sublist[1],
224 | ]);
225 | });
226 |
227 | it('conditional nth child combination', function () {
228 | let matches = esquery(customNodes, ':matches(:nth-child(2), :nth-last-child(2))', { visitorKeys });
229 | assert.includeMembers(matches, [
230 | customNodes.list[1],
231 | customNodes.list[2],
232 | customNodes.list[2].sublist[0],
233 | customNodes.list[2].sublist[1],
234 | customNodes.list[3].sublist[1],
235 | ]);
236 |
237 | matches = esquery(customNodes, ':not(:nth-child(2)):nth-last-child(2)', { visitorKeys });
238 | assert.includeMembers(matches, [
239 | customNodes.list[2],
240 | customNodes.list[2].sublist[0],
241 | ]);
242 |
243 |
244 | matches = esquery(customNodes, ':nth-last-child(2) > :nth-child(2)', { visitorKeys });
245 | assert.includeMembers(matches, [
246 | customNodes.list[2].sublist[1],
247 | ]);
248 |
249 | matches = esquery(customNodes, ':nth-last-child(2) :nth-child(2)', { visitorKeys });
250 | assert.includeMembers(matches, [
251 | customNodes.list[2].sublist[1],
252 | ]);
253 |
254 | matches = esquery(customNodes, '*:has(:nth-child(2))', { visitorKeys });
255 | assert.includeMembers(matches, [
256 | customNodes.list[2],
257 | customNodes.list[3],
258 | ]);
259 | });
260 | });
261 |
--------------------------------------------------------------------------------
/tests/queryAttribute.js:
--------------------------------------------------------------------------------
1 | import esquery from '../esquery.js';
2 | import literal from './fixtures/literal.js';
3 | import conditional from './fixtures/conditional.js';
4 | import forLoop from './fixtures/forLoop.js';
5 | import simpleFunction from './fixtures/simpleFunction.js';
6 | import simpleProgram from './fixtures/simpleProgram.js';
7 |
8 | describe('Attribute query', function () {
9 |
10 | it('conditional', function () {
11 | let matches = esquery(conditional, '[name="x"]');
12 | assert.includeMembers(matches, [
13 | conditional.body[0].test.left,
14 | conditional.body[0].alternate.body[0].expression.left,
15 | conditional.body[1].test.left.left.left,
16 | conditional.body[1].test.right
17 | ]);
18 |
19 | matches = esquery(conditional, '[callee.name="foo"]');
20 | assert.includeMembers(matches, [
21 | conditional.body[0].consequent.body[0].expression
22 | ]);
23 |
24 | matches = esquery(conditional, '[operator]');
25 | assert.includeMembers(matches, [
26 | conditional.body[0].test,
27 | conditional.body[0].alternate.body[0].expression,
28 | conditional.body[1].test,
29 | conditional.body[1].test.left,
30 | conditional.body[1].test.left.left
31 | ]);
32 |
33 | matches = esquery(conditional, '[prefix=true]');
34 | assert.includeMembers(matches, [
35 | conditional.body[1].consequent.body[0].expression.right
36 | ]);
37 | });
38 |
39 | it('literal with special escapes', function () {
40 | const matches = esquery(literal, 'Literal[value=\'\\b\\f\\n\\r\\t\\v and just a \\ back\\slash\']');
41 | assert.includeMembers(matches, [
42 | literal.body[0].declarations[0].init
43 | ]);
44 | });
45 |
46 | it('literal with decimal', function () {
47 | const matches = esquery(literal, 'Literal[value=21.35]');
48 | assert.includeMembers(matches, [
49 | literal.body[1].declarations[0].init
50 | ]);
51 | });
52 |
53 | it('literal with extra whitespace', function () {
54 | const matches = esquery(literal, 'Literal[value = 21.35]');
55 | assert.includeMembers(matches, [
56 | literal.body[1].declarations[0].init
57 | ]);
58 | });
59 |
60 | it('literal with backslashes', function () {
61 | const matches = esquery(literal, 'Literal[value="\\z"]');
62 | assert.includeMembers(matches, [
63 | literal.body[2].declarations[0].init
64 | ]);
65 | });
66 |
67 | it('literal with backslash after beginning', function () {
68 | const matches = esquery(literal, 'Literal[value="abc\\z"]');
69 | assert.includeMembers(matches, [
70 | literal.body[3].declarations[0].init
71 | ]);
72 | });
73 |
74 | it('for loop', function () {
75 | let matches = esquery(forLoop, '[operator="="]');
76 | assert.includeMembers(matches, [
77 | forLoop.body[0].init
78 | ]);
79 |
80 | matches = esquery(forLoop, '[object.name="foo"]');
81 | assert.includeMembers(matches, [
82 | forLoop.body[0].test.right
83 | ]);
84 |
85 | matches = esquery(forLoop, '[operator]');
86 | assert.includeMembers(matches, [
87 | forLoop.body[0].init,
88 | forLoop.body[0].test,
89 | forLoop.body[0].update
90 | ]);
91 | });
92 |
93 | it('simple function', function () {
94 | let matches = esquery(simpleFunction, '[kind="var"]');
95 | assert.includeMembers(matches, [
96 | simpleFunction.body[0].body.body[0]
97 | ]);
98 |
99 | matches = esquery(simpleFunction, '[id.name="foo"]');
100 | assert.includeMembers(matches, [
101 | simpleFunction.body[0]
102 | ]);
103 |
104 | matches = esquery(simpleFunction, '[left]');
105 | assert.includeMembers(matches, [
106 | simpleFunction.body[0].body.body[0].declarations[0].init
107 | ]);
108 | });
109 |
110 | it('simple program', function () {
111 | let matches = esquery(simpleProgram, '[kind="var"]');
112 | assert.includeMembers(matches, [
113 | simpleProgram.body[0],
114 | simpleProgram.body[1]
115 | ]);
116 |
117 | matches = esquery(simpleProgram, '[id.name="y"]');
118 | assert.includeMembers(matches, [
119 | simpleProgram.body[1].declarations[0]
120 | ]);
121 |
122 | matches = esquery(simpleProgram, '[body]');
123 | assert.includeMembers(matches, [
124 | simpleProgram,
125 | simpleProgram.body[3].consequent
126 | ]);
127 | });
128 |
129 | it('conditional regexp', function () {
130 | const matches = esquery(conditional, '[name=/x|foo/]');
131 | assert.includeMembers(matches, [
132 | conditional.body[0].test.left,
133 | conditional.body[0].consequent.body[0].expression.callee,
134 | conditional.body[0].alternate.body[0].expression.left
135 | ]);
136 | });
137 |
138 | it('simple function regexp', function () {
139 | const matches = esquery(simpleFunction, '[name=/x|foo/]');
140 | assert.includeMembers(matches, [
141 | simpleFunction.body[0].id,
142 | simpleFunction.body[0].params[0],
143 | simpleFunction.body[0].body.body[0].declarations[0].init.left
144 | ]);
145 | });
146 |
147 | it('simple function numeric', function () {
148 | const matches = esquery(simpleFunction, 'FunctionDeclaration[params.0.name=x]');
149 | assert.includeMembers(matches, [
150 | simpleFunction.body[0]
151 | ]);
152 | });
153 |
154 | it('simple program regexp', function () {
155 | const matches = esquery(simpleProgram, '[name=/[asdfy]/]');
156 | assert.includeMembers(matches, [
157 | simpleProgram.body[1].declarations[0].id,
158 | simpleProgram.body[3].test,
159 | simpleProgram.body[3].consequent.body[0].expression.left
160 | ]);
161 | });
162 |
163 | it('multiple regexp flags (i and u)', function () {
164 | const matches = esquery(simpleProgram, '[name=/\\u{61}|[SDFY]/iu]');
165 | assert.includeMembers(matches, [
166 | simpleProgram.body[1].declarations[0].id,
167 | simpleProgram.body[3].test,
168 | simpleProgram.body[3].consequent.body[0].expression.left
169 | ]);
170 | });
171 |
172 | if (parseInt(process.version) >= 8) {
173 | it('regexp flag (s)', function () {
174 | const matches = esquery(literal, '[value=/\f.\r/s]');
175 | assert.includeMembers(matches, [
176 | literal.body[0].declarations[0].init
177 | ]);
178 | });
179 | }
180 |
181 | it('regexp flag (m)', function () {
182 | const matches = esquery(literal, '[value=/^\r/m]');
183 | assert.includeMembers(matches, [
184 | literal.body[0].declarations[0].init
185 | ]);
186 | });
187 |
188 | it('for loop regexp', function () {
189 | const matches = esquery(forLoop, '[name=/i|foo/]');
190 | assert.includeMembers(matches, [
191 | forLoop.body[0].init.left,
192 | forLoop.body[0].test.left,
193 | forLoop.body[0].test.right.object,
194 | forLoop.body[0].update.argument,
195 | forLoop.body[0].body.body[0].expression.callee.object,
196 | forLoop.body[0].body.body[0].expression.callee.property
197 | ]);
198 | });
199 |
200 | it('nonexistent attribute regexp', function () {
201 | const matches = esquery(conditional, '[foobar=/./]');
202 | assert.lengthOf(matches, 0);
203 | });
204 |
205 | it('not string', function () {
206 | const matches = esquery(conditional, '[name!="x"]');
207 | assert.includeMembers(matches, [
208 | conditional.body[0].consequent.body[0].expression.callee,
209 | conditional.body[1].consequent.body[0].expression.left
210 | ]);
211 | });
212 |
213 | it('not type', function () {
214 | const matches = esquery(conditional, '[value!=type(number)]');
215 | assert.includeMembers(matches, [
216 | conditional.body[1].test.left.left.right,
217 | conditional.body[1].test.left.right,
218 | conditional.body[1].alternate
219 | ]);
220 | });
221 |
222 | it('not regexp', function () {
223 | const matches = esquery(conditional, '[name!=/x|y/]');
224 | assert.includeMembers(matches, [
225 | conditional.body[0].consequent.body[0].expression.callee
226 | ]);
227 | });
228 |
229 | it('less than', function () {
230 | const matches = esquery(conditional, '[body.length<2]');
231 | assert.includeMembers(matches, [
232 | conditional.body[0].consequent,
233 | conditional.body[0].alternate,
234 | conditional.body[1].consequent,
235 | conditional.body[1].alternate.consequent
236 | ]);
237 | });
238 |
239 | it('greater than', function () {
240 | const matches = esquery(conditional, '[body.length>1]');
241 | assert.includeMembers(matches, [
242 | conditional
243 | ]);
244 | });
245 |
246 | it('less than or equal', function () {
247 | const matches = esquery(conditional, '[body.length<=2]');
248 | assert.includeMembers(matches, [
249 | conditional,
250 | conditional.body[0].consequent,
251 | conditional.body[0].alternate,
252 | conditional.body[1].consequent,
253 | conditional.body[1].alternate.consequent
254 | ]);
255 | });
256 |
257 | it('greater than or equal', function () {
258 | const matches = esquery(conditional, '[body.length>=1]');
259 | assert.includeMembers(matches, [
260 | conditional,
261 | conditional.body[0].consequent,
262 | conditional.body[0].alternate,
263 | conditional.body[1].consequent,
264 | conditional.body[1].alternate.consequent
265 | ]);
266 | });
267 |
268 | it('attribute type', function () {
269 | let matches = esquery(conditional, '[test=type(object)]');
270 | assert.includeMembers(matches, [
271 | conditional.body[0],
272 | conditional.body[1],
273 | conditional.body[1].alternate
274 | ]);
275 |
276 | matches = esquery(conditional, '[value=type(boolean)]');
277 | assert.includeMembers(matches, [
278 | conditional.body[1].test.left.right,
279 | conditional.body[1].alternate.test
280 | ]);
281 | });
282 |
283 | });
284 |
--------------------------------------------------------------------------------
/esquery.js:
--------------------------------------------------------------------------------
1 | /* vim: set sw=4 sts=4 : */
2 | import estraverse from 'estraverse';
3 | import parser from './parser.js';
4 |
5 | /**
6 | * @typedef {"LEFT_SIDE"|"RIGHT_SIDE"} Side
7 | */
8 |
9 | const LEFT_SIDE = 'LEFT_SIDE';
10 | const RIGHT_SIDE = 'RIGHT_SIDE';
11 |
12 | /**
13 | * @external AST
14 | * @see https://esprima.readthedocs.io/en/latest/syntax-tree-format.html
15 | */
16 |
17 | /**
18 | * One of the rules of `grammar.pegjs`
19 | * @typedef {PlainObject} SelectorAST
20 | * @see grammar.pegjs
21 | */
22 |
23 | /**
24 | * The `sequence` production of `grammar.pegjs`
25 | * @typedef {PlainObject} SelectorSequenceAST
26 | */
27 |
28 | /**
29 | * Get the value of a property which may be multiple levels down
30 | * in the object.
31 | * @param {?PlainObject} obj
32 | * @param {string[]} keys
33 | * @returns {undefined|boolean|string|number|external:AST}
34 | */
35 | function getPath(obj, keys) {
36 | for (let i = 0; i < keys.length; ++i) {
37 | if (obj == null) { return obj; }
38 | obj = obj[keys[i]];
39 | }
40 | return obj;
41 | }
42 |
43 | /**
44 | * Determine whether `node` can be reached by following `path`,
45 | * starting at `ancestor`.
46 | * @param {?external:AST} node
47 | * @param {?external:AST} ancestor
48 | * @param {string[]} path
49 | * @param {Integer} fromPathIndex
50 | * @returns {boolean}
51 | */
52 | function inPath(node, ancestor, path, fromPathIndex) {
53 | let current = ancestor;
54 | for (let i = fromPathIndex; i < path.length; ++i) {
55 | if (current == null) {
56 | return false;
57 | }
58 | const field = current[path[i]];
59 | if (Array.isArray(field)) {
60 | for (let k = 0; k < field.length; ++k) {
61 | if (inPath(node, field[k], path, i + 1)) {
62 | return true;
63 | }
64 | }
65 | return false;
66 | }
67 | current = field;
68 | }
69 | return node === current;
70 | }
71 |
72 | /**
73 | * A generated matcher function for a selector.
74 | * @callback SelectorMatcher
75 | * @param {?SelectorAST} selector
76 | * @param {external:AST[]} [ancestry=[]]
77 | * @param {ESQueryOptions} [options]
78 | * @returns {void}
79 | */
80 |
81 | /**
82 | * A WeakMap for holding cached matcher functions for selectors.
83 | * @type {WeakMap}
84 | */
85 | const MATCHER_CACHE = typeof WeakMap === 'function' ? new WeakMap : null;
86 |
87 | /**
88 | * Look up a matcher function for `selector` in the cache.
89 | * If it does not exist, generate it with `generateMatcher` and add it to the cache.
90 | * In engines without WeakMap, the caching is skipped and matchers are generated with every call.
91 | * @param {?SelectorAST} selector
92 | * @returns {SelectorMatcher}
93 | */
94 | function getMatcher(selector) {
95 | if (selector == null) {
96 | return () => true;
97 | }
98 |
99 | if (MATCHER_CACHE != null) {
100 | let matcher = MATCHER_CACHE.get(selector);
101 | if (matcher != null) {
102 | return matcher;
103 | }
104 | matcher = generateMatcher(selector);
105 | MATCHER_CACHE.set(selector, matcher);
106 | return matcher;
107 | }
108 |
109 | return generateMatcher(selector);
110 | }
111 |
112 | /**
113 | * Create a matcher function for `selector`,
114 | * @param {?SelectorAST} selector
115 | * @returns {SelectorMatcher}
116 | */
117 | function generateMatcher(selector) {
118 | switch(selector.type) {
119 | case 'wildcard':
120 | return () => true;
121 |
122 | case 'identifier': {
123 | const value = selector.value.toLowerCase();
124 | return (node, ancestry, options) => {
125 | const nodeTypeKey = (options && options.nodeTypeKey) || 'type';
126 | return value === node[nodeTypeKey].toLowerCase();
127 | };
128 | }
129 |
130 | case 'exactNode':
131 | return (node, ancestry) => {
132 | return ancestry.length === 0;
133 | };
134 |
135 | case 'field': {
136 | const path = selector.name.split('.');
137 | return (node, ancestry) => {
138 | const ancestor = ancestry[path.length - 1];
139 | return inPath(node, ancestor, path, 0);
140 | };
141 | }
142 |
143 | case 'matches': {
144 | const matchers = selector.selectors.map(getMatcher);
145 | return (node, ancestry, options) => {
146 | for (let i = 0; i < matchers.length; ++i) {
147 | if (matchers[i](node, ancestry, options)) { return true; }
148 | }
149 | return false;
150 | };
151 | }
152 |
153 | case 'compound': {
154 | const matchers = selector.selectors.map(getMatcher);
155 | return (node, ancestry, options) => {
156 | for (let i = 0; i < matchers.length; ++i) {
157 | if (!matchers[i](node, ancestry, options)) { return false; }
158 | }
159 | return true;
160 | };
161 | }
162 |
163 | case 'not': {
164 | const matchers = selector.selectors.map(getMatcher);
165 | return (node, ancestry, options) => {
166 | for (let i = 0; i < matchers.length; ++i) {
167 | if (matchers[i](node, ancestry, options)) { return false; }
168 | }
169 | return true;
170 | };
171 | }
172 |
173 | case 'has': {
174 | const matchers = selector.selectors.map(getMatcher);
175 | return (node, ancestry, options) => {
176 | let result = false;
177 |
178 | const a = [];
179 | estraverse.traverse(node, {
180 | enter (node, parent) {
181 | if (parent != null) { a.unshift(parent); }
182 |
183 | for (let i = 0; i < matchers.length; ++i) {
184 | if (matchers[i](node, a, options)) {
185 | result = true;
186 | this.break();
187 | return;
188 | }
189 | }
190 | },
191 | leave () { a.shift(); },
192 | keys: options && options.visitorKeys,
193 | fallback: options && options.fallback || 'iteration'
194 | });
195 |
196 | return result;
197 | };
198 | }
199 |
200 | case 'child': {
201 | const left = getMatcher(selector.left);
202 | const right = getMatcher(selector.right);
203 | return (node, ancestry, options) => {
204 | if (ancestry.length > 0 && right(node, ancestry, options)) {
205 | return left(ancestry[0], ancestry.slice(1), options);
206 | }
207 | return false;
208 | };
209 | }
210 |
211 | case 'descendant': {
212 | const left = getMatcher(selector.left);
213 | const right = getMatcher(selector.right);
214 | return (node, ancestry, options) => {
215 | if (right(node, ancestry, options)) {
216 | for (let i = 0, l = ancestry.length; i < l; ++i) {
217 | if (left(ancestry[i], ancestry.slice(i + 1), options)) {
218 | return true;
219 | }
220 | }
221 | }
222 | return false;
223 | };
224 | }
225 |
226 | case 'attribute': {
227 | const path = selector.name.split('.');
228 | switch (selector.operator) {
229 | case void 0:
230 | return (node) => getPath(node, path) != null;
231 | case '=':
232 | switch (selector.value.type) {
233 | case 'regexp':
234 | return (node) => {
235 | const p = getPath(node, path);
236 | return typeof p === 'string' && selector.value.value.test(p);
237 | };
238 | case 'literal': {
239 | const literal = `${selector.value.value}`;
240 | return (node) => literal === `${getPath(node, path)}`;
241 | }
242 | case 'type':
243 | return (node) => selector.value.value === typeof getPath(node, path);
244 | }
245 | throw new Error(`Unknown selector value type: ${selector.value.type}`);
246 | case '!=':
247 | switch (selector.value.type) {
248 | case 'regexp':
249 | return (node) => !selector.value.value.test(getPath(node, path));
250 | case 'literal': {
251 | const literal = `${selector.value.value}`;
252 | return (node) => literal !== `${getPath(node, path)}`;
253 | }
254 | case 'type':
255 | return (node) => selector.value.value !== typeof getPath(node, path);
256 | }
257 | throw new Error(`Unknown selector value type: ${selector.value.type}`);
258 | case '<=':
259 | return (node) => getPath(node, path) <= selector.value.value;
260 | case '<':
261 | return (node) => getPath(node, path) < selector.value.value;
262 | case '>':
263 | return (node) => getPath(node, path) > selector.value.value;
264 | case '>=':
265 | return (node) => getPath(node, path) >= selector.value.value;
266 | }
267 | throw new Error(`Unknown operator: ${selector.operator}`);
268 | }
269 |
270 | case 'sibling': {
271 | const left = getMatcher(selector.left);
272 | const right = getMatcher(selector.right);
273 | return (node, ancestry, options) =>
274 | right(node, ancestry, options) &&
275 | sibling(node, left, ancestry, LEFT_SIDE, options) ||
276 | selector.left.subject &&
277 | left(node, ancestry, options) &&
278 | sibling(node, right, ancestry, RIGHT_SIDE, options);
279 | }
280 |
281 | case 'adjacent': {
282 | const left = getMatcher(selector.left);
283 | const right = getMatcher(selector.right);
284 | return (node, ancestry, options) =>
285 | right(node, ancestry, options) &&
286 | adjacent(node, left, ancestry, LEFT_SIDE, options) ||
287 | selector.right.subject &&
288 | left(node, ancestry, options) &&
289 | adjacent(node, right, ancestry, RIGHT_SIDE, options);
290 | }
291 |
292 | case 'nth-child': {
293 | const nth = selector.index.value;
294 | const right = getMatcher(selector.right);
295 | return (node, ancestry, options) =>
296 | right(node, ancestry, options) &&
297 | nthChild(node, ancestry, nth, options);
298 | }
299 |
300 | case 'nth-last-child': {
301 | const nth = -selector.index.value;
302 | const right = getMatcher(selector.right);
303 | return (node, ancestry, options) =>
304 | right(node, ancestry, options) &&
305 | nthChild(node, ancestry, nth, options);
306 | }
307 |
308 | case 'class': {
309 |
310 | const name = selector.name.toLowerCase();
311 |
312 | return (node, ancestry, options) => {
313 |
314 | if (options && options.matchClass) {
315 | return options.matchClass(selector.name, node, ancestry);
316 | }
317 |
318 | if (options && options.nodeTypeKey) return false;
319 |
320 | switch(name){
321 | case 'statement':
322 | if(node.type.slice(-9) === 'Statement') return true;
323 | // fallthrough: interface Declaration <: Statement { }
324 | case 'declaration':
325 | return node.type.slice(-11) === 'Declaration';
326 | case 'pattern':
327 | if(node.type.slice(-7) === 'Pattern') return true;
328 | // fallthrough: interface Expression <: Node, Pattern { }
329 | case 'expression':
330 | return node.type.slice(-10) === 'Expression' ||
331 | node.type.slice(-7) === 'Literal' ||
332 | (
333 | node.type === 'Identifier' &&
334 | (ancestry.length === 0 || ancestry[0].type !== 'MetaProperty')
335 | ) ||
336 | node.type === 'MetaProperty';
337 | case 'function':
338 | return node.type === 'FunctionDeclaration' ||
339 | node.type === 'FunctionExpression' ||
340 | node.type === 'ArrowFunctionExpression';
341 | }
342 | throw new Error(`Unknown class name: ${selector.name}`);
343 | };
344 | }
345 | }
346 |
347 | throw new Error(`Unknown selector type: ${selector.type}`);
348 | }
349 |
350 | /**
351 | * @callback TraverseOptionFallback
352 | * @param {external:AST} node The given node.
353 | * @returns {string[]} An array of visitor keys for the given node.
354 | */
355 |
356 | /**
357 | * @callback ClassMatcher
358 | * @param {string} className The name of the class to match.
359 | * @param {external:AST} node The node to match against.
360 | * @param {Array} ancestry The ancestry of the node.
361 | * @returns {boolean} True if the node matches the class, false if not.
362 | */
363 |
364 | /**
365 | * @typedef {object} ESQueryOptions
366 | * @property {string} [nodeTypeKey="type"] By passing `nodeTypeKey`, we can allow other ASTs to use ESQuery.
367 | * @property { { [nodeType: string]: string[] } } [visitorKeys] By passing `visitorKeys` mapping, we can extend the properties of the nodes that traverse the node.
368 | * @property {TraverseOptionFallback} [fallback] By passing `fallback` option, we can control the properties of traversing nodes when encountering unknown nodes.
369 | * @property {ClassMatcher} [matchClass] By passing `matchClass` option, we can customize the interpretation of classes.
370 | */
371 |
372 | /**
373 | * Given a `node` and its ancestors, determine if `node` is matched
374 | * by `selector`.
375 | * @param {?external:AST} node
376 | * @param {?SelectorAST} selector
377 | * @param {external:AST[]} [ancestry=[]]
378 | * @param {ESQueryOptions} [options]
379 | * @throws {Error} Unknowns (operator, class name, selector type, or
380 | * selector value type)
381 | * @returns {boolean}
382 | */
383 | function matches(node, selector, ancestry, options) {
384 | if (!selector) { return true; }
385 | if (!node) { return false; }
386 | if (!ancestry) { ancestry = []; }
387 |
388 | return getMatcher(selector)(node, ancestry, options);
389 | }
390 |
391 | /**
392 | * Get visitor keys of a given node.
393 | * @param {external:AST} node The AST node to get keys.
394 | * @param {ESQueryOptions|undefined} options
395 | * @returns {string[]} Visitor keys of the node.
396 | */
397 | function getVisitorKeys(node, options) {
398 | const nodeTypeKey = (options && options.nodeTypeKey) || 'type';
399 |
400 | const nodeType = node[nodeTypeKey];
401 | if (options && options.visitorKeys && options.visitorKeys[nodeType]) {
402 | return options.visitorKeys[nodeType];
403 | }
404 | if (estraverse.VisitorKeys[nodeType]) {
405 | return estraverse.VisitorKeys[nodeType];
406 | }
407 | if (options && typeof options.fallback === 'function') {
408 | return options.fallback(node);
409 | }
410 | // 'iteration' fallback
411 | return Object.keys(node).filter(function (key) {
412 | return key !== nodeTypeKey;
413 | });
414 | }
415 |
416 |
417 | /**
418 | * Check whether the given value is an ASTNode or not.
419 | * @param {any} node The value to check.
420 | * @param {ESQueryOptions|undefined} options The options to use.
421 | * @returns {boolean} `true` if the value is an ASTNode.
422 | */
423 | function isNode(node, options) {
424 | const nodeTypeKey = (options && options.nodeTypeKey) || 'type';
425 | return node !== null && typeof node === 'object' && typeof node[nodeTypeKey] === 'string';
426 | }
427 |
428 | /**
429 | * Determines if the given node has a sibling that matches the
430 | * given selector matcher.
431 | * @param {external:AST} node
432 | * @param {SelectorMatcher} matcher
433 | * @param {external:AST[]} ancestry
434 | * @param {Side} side
435 | * @param {ESQueryOptions|undefined} options
436 | * @returns {boolean}
437 | */
438 | function sibling(node, matcher, ancestry, side, options) {
439 | const [parent] = ancestry;
440 | if (!parent) { return false; }
441 | const keys = getVisitorKeys(parent, options);
442 | for (let i = 0; i < keys.length; ++i) {
443 | const listProp = parent[keys[i]];
444 | if (Array.isArray(listProp)) {
445 | const startIndex = listProp.indexOf(node);
446 | if (startIndex < 0) { continue; }
447 | let lowerBound, upperBound;
448 | if (side === LEFT_SIDE) {
449 | lowerBound = 0;
450 | upperBound = startIndex;
451 | } else {
452 | lowerBound = startIndex + 1;
453 | upperBound = listProp.length;
454 | }
455 | for (let k = lowerBound; k < upperBound; ++k) {
456 | if (isNode(listProp[k], options) && matcher(listProp[k], ancestry, options)) {
457 | return true;
458 | }
459 | }
460 | }
461 | }
462 | return false;
463 | }
464 |
465 | /**
466 | * Determines if the given node has an adjacent sibling that matches
467 | * the given selector matcher.
468 | * @param {external:AST} node
469 | * @param {SelectorMatcher} matcher
470 | * @param {external:AST[]} ancestry
471 | * @param {Side} side
472 | * @param {ESQueryOptions|undefined} options
473 | * @returns {boolean}
474 | */
475 | function adjacent(node, matcher, ancestry, side, options) {
476 | const [parent] = ancestry;
477 | if (!parent) { return false; }
478 | const keys = getVisitorKeys(parent, options);
479 | for (let i = 0; i < keys.length; ++i) {
480 | const listProp = parent[keys[i]];
481 | if (Array.isArray(listProp)) {
482 | const idx = listProp.indexOf(node);
483 | if (idx < 0) { continue; }
484 | if (side === LEFT_SIDE && idx > 0 && isNode(listProp[idx - 1], options) && matcher(listProp[idx - 1], ancestry, options)) {
485 | return true;
486 | }
487 | if (side === RIGHT_SIDE && idx < listProp.length - 1 && isNode(listProp[idx + 1], options) && matcher(listProp[idx + 1], ancestry, options)) {
488 | return true;
489 | }
490 | }
491 | }
492 | return false;
493 | }
494 |
495 | /**
496 | * Determines if the given node is the `nth` child.
497 | * If `nth` is negative then the position is counted
498 | * from the end of the list of children.
499 | * @param {external:AST} node
500 | * @param {external:AST[]} ancestry
501 | * @param {Integer} nth
502 | * @param {ESQueryOptions|undefined} options
503 | * @returns {boolean}
504 | */
505 | function nthChild(node, ancestry, nth, options) {
506 | if (nth === 0) { return false; }
507 | const [parent] = ancestry;
508 | if (!parent) { return false; }
509 | const keys = getVisitorKeys(parent, options);
510 | for (let i = 0; i < keys.length; ++i) {
511 | const listProp = parent[keys[i]];
512 | if (Array.isArray(listProp)){
513 | const idx = nth < 0 ? listProp.length + nth : nth - 1;
514 | if (idx >= 0 && idx < listProp.length && listProp[idx] === node) {
515 | return true;
516 | }
517 | }
518 | }
519 | return false;
520 | }
521 |
522 | /**
523 | * For each selector node marked as a subject, find the portion of the
524 | * selector that the subject must match.
525 | * @param {SelectorAST} selector
526 | * @param {SelectorAST} [ancestor] Defaults to `selector`
527 | * @returns {SelectorAST[]}
528 | */
529 | function subjects(selector, ancestor) {
530 | if (selector == null || typeof selector != 'object') { return []; }
531 | if (ancestor == null) { ancestor = selector; }
532 | const results = selector.subject ? [ancestor] : [];
533 | const keys = Object.keys(selector);
534 | for (let i = 0; i < keys.length; ++i) {
535 | const p = keys[i];
536 | const sel = selector[p];
537 | results.push(...subjects(sel, p === 'left' ? sel : ancestor));
538 | }
539 | return results;
540 | }
541 |
542 | /**
543 | * @callback TraverseVisitor
544 | * @param {?external:AST} node
545 | * @param {?external:AST} parent
546 | * @param {external:AST[]} ancestry
547 | */
548 |
549 | /**
550 | * From a JS AST and a selector AST, collect all JS AST nodes that
551 | * match the selector.
552 | * @param {external:AST} ast
553 | * @param {?SelectorAST} selector
554 | * @param {TraverseVisitor} visitor
555 | * @param {ESQueryOptions} [options]
556 | * @returns {external:AST[]}
557 | */
558 | function traverse(ast, selector, visitor, options) {
559 | if (!selector) { return; }
560 | const ancestry = [];
561 | const matcher = getMatcher(selector);
562 | const altSubjects = subjects(selector).map(getMatcher);
563 | estraverse.traverse(ast, {
564 | enter (node, parent) {
565 | if (parent != null) { ancestry.unshift(parent); }
566 | if (matcher(node, ancestry, options)) {
567 | if (altSubjects.length) {
568 | for (let i = 0, l = altSubjects.length; i < l; ++i) {
569 | if (altSubjects[i](node, ancestry, options)) {
570 | visitor(node, parent, ancestry);
571 | }
572 | for (let k = 0, m = ancestry.length; k < m; ++k) {
573 | const succeedingAncestry = ancestry.slice(k + 1);
574 | if (altSubjects[i](ancestry[k], succeedingAncestry, options)) {
575 | visitor(ancestry[k], parent, succeedingAncestry);
576 | }
577 | }
578 | }
579 | } else {
580 | visitor(node, parent, ancestry);
581 | }
582 | }
583 | },
584 | leave () { ancestry.shift(); },
585 | keys: options && options.visitorKeys,
586 | fallback: options && options.fallback || 'iteration'
587 | });
588 | }
589 |
590 |
591 | /**
592 | * From a JS AST and a selector AST, collect all JS AST nodes that
593 | * match the selector.
594 | * @param {external:AST} ast
595 | * @param {?SelectorAST} selector
596 | * @param {ESQueryOptions} [options]
597 | * @returns {external:AST[]}
598 | */
599 | function match(ast, selector, options) {
600 | const results = [];
601 | traverse(ast, selector, function (node) {
602 | results.push(node);
603 | }, options);
604 | return results;
605 | }
606 |
607 | /**
608 | * Parse a selector string and return its AST.
609 | * @param {string} selector
610 | * @returns {SelectorAST}
611 | */
612 | function parse(selector) {
613 | return parser.parse(selector);
614 | }
615 |
616 | /**
617 | * Query the code AST using the selector string.
618 | * @param {external:AST} ast
619 | * @param {string} selector
620 | * @param {ESQueryOptions} [options]
621 | * @returns {external:AST[]}
622 | */
623 | function query(ast, selector, options) {
624 | return match(ast, parse(selector), options);
625 | }
626 |
627 | query.parse = parse;
628 | query.match = match;
629 | query.traverse = traverse;
630 | query.matches = matches;
631 | query.query = query;
632 |
633 | export default query;
634 |
--------------------------------------------------------------------------------
/parser.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Generated by PEG.js 0.10.0.
3 | *
4 | * http://pegjs.org/
5 | */
6 | (function(root, factory) {
7 | if (typeof define === "function" && define.amd) {
8 | define([], factory);
9 | } else if (typeof module === "object" && module.exports) {
10 | module.exports = factory();
11 | }
12 | })(this, function() {
13 | "use strict";
14 |
15 | function peg$subclass(child, parent) {
16 | function ctor() { this.constructor = child; }
17 | ctor.prototype = parent.prototype;
18 | child.prototype = new ctor();
19 | }
20 |
21 | function peg$SyntaxError(message, expected, found, location) {
22 | this.message = message;
23 | this.expected = expected;
24 | this.found = found;
25 | this.location = location;
26 | this.name = "SyntaxError";
27 |
28 | if (typeof Error.captureStackTrace === "function") {
29 | Error.captureStackTrace(this, peg$SyntaxError);
30 | }
31 | }
32 |
33 | peg$subclass(peg$SyntaxError, Error);
34 |
35 | peg$SyntaxError.buildMessage = function(expected, found) {
36 | var DESCRIBE_EXPECTATION_FNS = {
37 | literal: function(expectation) {
38 | return "\"" + literalEscape(expectation.text) + "\"";
39 | },
40 |
41 | "class": function(expectation) {
42 | var escapedParts = "",
43 | i;
44 |
45 | for (i = 0; i < expectation.parts.length; i++) {
46 | escapedParts += expectation.parts[i] instanceof Array
47 | ? classEscape(expectation.parts[i][0]) + "-" + classEscape(expectation.parts[i][1])
48 | : classEscape(expectation.parts[i]);
49 | }
50 |
51 | return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
52 | },
53 |
54 | any: function(expectation) {
55 | return "any character";
56 | },
57 |
58 | end: function(expectation) {
59 | return "end of input";
60 | },
61 |
62 | other: function(expectation) {
63 | return expectation.description;
64 | }
65 | };
66 |
67 | function hex(ch) {
68 | return ch.charCodeAt(0).toString(16).toUpperCase();
69 | }
70 |
71 | function literalEscape(s) {
72 | return s
73 | .replace(/\\/g, '\\\\')
74 | .replace(/"/g, '\\"')
75 | .replace(/\0/g, '\\0')
76 | .replace(/\t/g, '\\t')
77 | .replace(/\n/g, '\\n')
78 | .replace(/\r/g, '\\r')
79 | .replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
80 | .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); });
81 | }
82 |
83 | function classEscape(s) {
84 | return s
85 | .replace(/\\/g, '\\\\')
86 | .replace(/\]/g, '\\]')
87 | .replace(/\^/g, '\\^')
88 | .replace(/-/g, '\\-')
89 | .replace(/\0/g, '\\0')
90 | .replace(/\t/g, '\\t')
91 | .replace(/\n/g, '\\n')
92 | .replace(/\r/g, '\\r')
93 | .replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
94 | .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); });
95 | }
96 |
97 | function describeExpectation(expectation) {
98 | return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
99 | }
100 |
101 | function describeExpected(expected) {
102 | var descriptions = new Array(expected.length),
103 | i, j;
104 |
105 | for (i = 0; i < expected.length; i++) {
106 | descriptions[i] = describeExpectation(expected[i]);
107 | }
108 |
109 | descriptions.sort();
110 |
111 | if (descriptions.length > 0) {
112 | for (i = 1, j = 1; i < descriptions.length; i++) {
113 | if (descriptions[i - 1] !== descriptions[i]) {
114 | descriptions[j] = descriptions[i];
115 | j++;
116 | }
117 | }
118 | descriptions.length = j;
119 | }
120 |
121 | switch (descriptions.length) {
122 | case 1:
123 | return descriptions[0];
124 |
125 | case 2:
126 | return descriptions[0] + " or " + descriptions[1];
127 |
128 | default:
129 | return descriptions.slice(0, -1).join(", ")
130 | + ", or "
131 | + descriptions[descriptions.length - 1];
132 | }
133 | }
134 |
135 | function describeFound(found) {
136 | return found ? "\"" + literalEscape(found) + "\"" : "end of input";
137 | }
138 |
139 | return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
140 | };
141 |
142 | function peg$parse(input, options) {
143 | options = options !== void 0 ? options : {};
144 |
145 | var peg$FAILED = {},
146 |
147 | peg$startRuleFunctions = { start: peg$parsestart },
148 | peg$startRuleFunction = peg$parsestart,
149 |
150 | peg$c0 = function(ss) {
151 | return ss.length === 1 ? ss[0] : { type: 'matches', selectors: ss };
152 | },
153 | peg$c1 = function() { return void 0; },
154 | peg$c2 = " ",
155 | peg$c3 = peg$literalExpectation(" ", false),
156 | peg$c4 = /^[^ [\],():#!=><~+.]/,
157 | peg$c5 = peg$classExpectation([" ", "[", "]", ",", "(", ")", ":", "#", "!", "=", ">", "<", "~", "+", "."], true, false),
158 | peg$c6 = function(i) { return i.join(''); },
159 | peg$c7 = ">",
160 | peg$c8 = peg$literalExpectation(">", false),
161 | peg$c9 = function() { return 'child'; },
162 | peg$c10 = "~",
163 | peg$c11 = peg$literalExpectation("~", false),
164 | peg$c12 = function() { return 'sibling'; },
165 | peg$c13 = "+",
166 | peg$c14 = peg$literalExpectation("+", false),
167 | peg$c15 = function() { return 'adjacent'; },
168 | peg$c16 = function() { return 'descendant'; },
169 | peg$c17 = ",",
170 | peg$c18 = peg$literalExpectation(",", false),
171 | peg$c19 = function(s, ss) {
172 | return [s].concat(ss.map(function (s) { return s[3]; }));
173 | },
174 | peg$c20 = function(op, s) {
175 | if (!op) return s;
176 | return { type: op, left: { type: 'exactNode' }, right: s };
177 | },
178 | peg$c21 = function(a, ops) {
179 | return ops.reduce(function (memo, rhs) {
180 | return { type: rhs[0], left: memo, right: rhs[1] };
181 | }, a);
182 | },
183 | peg$c22 = "!",
184 | peg$c23 = peg$literalExpectation("!", false),
185 | peg$c24 = function(subject, as) {
186 | const b = as.length === 1 ? as[0] : { type: 'compound', selectors: as };
187 | if(subject) b.subject = true;
188 | return b;
189 | },
190 | peg$c25 = "*",
191 | peg$c26 = peg$literalExpectation("*", false),
192 | peg$c27 = function(a) { return { type: 'wildcard', value: a }; },
193 | peg$c28 = "#",
194 | peg$c29 = peg$literalExpectation("#", false),
195 | peg$c30 = function(i) { return { type: 'identifier', value: i }; },
196 | peg$c31 = "[",
197 | peg$c32 = peg$literalExpectation("[", false),
198 | peg$c33 = "]",
199 | peg$c34 = peg$literalExpectation("]", false),
200 | peg$c35 = function(v) { return v; },
201 | peg$c36 = /^[>", "<", "!"], false, false),
203 | peg$c38 = "=",
204 | peg$c39 = peg$literalExpectation("=", false),
205 | peg$c40 = function(a) { return (a || '') + '='; },
206 | peg$c41 = /^[><]/,
207 | peg$c42 = peg$classExpectation([">", "<"], false, false),
208 | peg$c43 = ".",
209 | peg$c44 = peg$literalExpectation(".", false),
210 | peg$c45 = function(a, as) {
211 | return [].concat.apply([a], as).join('');
212 | },
213 | peg$c46 = function(name, op, value) {
214 | return { type: 'attribute', name: name, operator: op, value: value };
215 | },
216 | peg$c47 = function(name) { return { type: 'attribute', name: name }; },
217 | peg$c48 = "\"",
218 | peg$c49 = peg$literalExpectation("\"", false),
219 | peg$c50 = /^[^\\"]/,
220 | peg$c51 = peg$classExpectation(["\\", "\""], true, false),
221 | peg$c52 = "\\",
222 | peg$c53 = peg$literalExpectation("\\", false),
223 | peg$c54 = peg$anyExpectation(),
224 | peg$c55 = function(a, b) { return a + b; },
225 | peg$c56 = function(d) {
226 | return { type: 'literal', value: strUnescape(d.join('')) };
227 | },
228 | peg$c57 = "'",
229 | peg$c58 = peg$literalExpectation("'", false),
230 | peg$c59 = /^[^\\']/,
231 | peg$c60 = peg$classExpectation(["\\", "'"], true, false),
232 | peg$c61 = /^[0-9]/,
233 | peg$c62 = peg$classExpectation([["0", "9"]], false, false),
234 | peg$c63 = function(a, b) {
235 | // Can use `a.flat().join('')` once supported
236 | const leadingDecimals = a ? [].concat.apply([], a).join('') : '';
237 | return { type: 'literal', value: parseFloat(leadingDecimals + b.join('')) };
238 | },
239 | peg$c64 = function(i) { return { type: 'literal', value: i }; },
240 | peg$c65 = "type(",
241 | peg$c66 = peg$literalExpectation("type(", false),
242 | peg$c67 = /^[^ )]/,
243 | peg$c68 = peg$classExpectation([" ", ")"], true, false),
244 | peg$c69 = ")",
245 | peg$c70 = peg$literalExpectation(")", false),
246 | peg$c71 = function(t) { return { type: 'type', value: t.join('') }; },
247 | peg$c72 = /^[imsu]/,
248 | peg$c73 = peg$classExpectation(["i", "m", "s", "u"], false, false),
249 | peg$c74 = "/",
250 | peg$c75 = peg$literalExpectation("/", false),
251 | peg$c76 = /^[^\/]/,
252 | peg$c77 = peg$classExpectation(["/"], true, false),
253 | peg$c78 = function(d, flgs) { return {
254 | type: 'regexp', value: new RegExp(d.join(''), flgs ? flgs.join('') : '') };
255 | },
256 | peg$c79 = function(i, is) {
257 | return { type: 'field', name: is.reduce(function(memo, p){ return memo + p[0] + p[1]; }, i)};
258 | },
259 | peg$c80 = ":not(",
260 | peg$c81 = peg$literalExpectation(":not(", false),
261 | peg$c82 = function(ss) { return { type: 'not', selectors: ss }; },
262 | peg$c83 = ":matches(",
263 | peg$c84 = peg$literalExpectation(":matches(", false),
264 | peg$c85 = function(ss) { return { type: 'matches', selectors: ss }; },
265 | peg$c86 = ":is(",
266 | peg$c87 = peg$literalExpectation(":is(", false),
267 | peg$c88 = ":has(",
268 | peg$c89 = peg$literalExpectation(":has(", false),
269 | peg$c90 = function(ss) { return { type: 'has', selectors: ss }; },
270 | peg$c91 = ":first-child",
271 | peg$c92 = peg$literalExpectation(":first-child", false),
272 | peg$c93 = function() { return nth(1); },
273 | peg$c94 = ":last-child",
274 | peg$c95 = peg$literalExpectation(":last-child", false),
275 | peg$c96 = function() { return nthLast(1); },
276 | peg$c97 = ":nth-child(",
277 | peg$c98 = peg$literalExpectation(":nth-child(", false),
278 | peg$c99 = function(n) { return nth(parseInt(n.join(''), 10)); },
279 | peg$c100 = ":nth-last-child(",
280 | peg$c101 = peg$literalExpectation(":nth-last-child(", false),
281 | peg$c102 = function(n) { return nthLast(parseInt(n.join(''), 10)); },
282 | peg$c103 = ":",
283 | peg$c104 = peg$literalExpectation(":", false),
284 | peg$c105 = function(c) {
285 | return { type: 'class', name: c };
286 | },
287 |
288 | peg$currPos = 0,
289 | peg$savedPos = 0,
290 | peg$posDetailsCache = [{ line: 1, column: 1 }],
291 | peg$maxFailPos = 0,
292 | peg$maxFailExpected = [],
293 | peg$silentFails = 0,
294 |
295 | peg$resultsCache = {},
296 |
297 | peg$result;
298 |
299 | if ("startRule" in options) {
300 | if (!(options.startRule in peg$startRuleFunctions)) {
301 | throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
302 | }
303 |
304 | peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
305 | }
306 |
307 | function text() {
308 | return input.substring(peg$savedPos, peg$currPos);
309 | }
310 |
311 | function location() {
312 | return peg$computeLocation(peg$savedPos, peg$currPos);
313 | }
314 |
315 | function expected(description, location) {
316 | location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)
317 |
318 | throw peg$buildStructuredError(
319 | [peg$otherExpectation(description)],
320 | input.substring(peg$savedPos, peg$currPos),
321 | location
322 | );
323 | }
324 |
325 | function error(message, location) {
326 | location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)
327 |
328 | throw peg$buildSimpleError(message, location);
329 | }
330 |
331 | function peg$literalExpectation(text, ignoreCase) {
332 | return { type: "literal", text: text, ignoreCase: ignoreCase };
333 | }
334 |
335 | function peg$classExpectation(parts, inverted, ignoreCase) {
336 | return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };
337 | }
338 |
339 | function peg$anyExpectation() {
340 | return { type: "any" };
341 | }
342 |
343 | function peg$endExpectation() {
344 | return { type: "end" };
345 | }
346 |
347 | function peg$otherExpectation(description) {
348 | return { type: "other", description: description };
349 | }
350 |
351 | function peg$computePosDetails(pos) {
352 | var details = peg$posDetailsCache[pos], p;
353 |
354 | if (details) {
355 | return details;
356 | } else {
357 | p = pos - 1;
358 | while (!peg$posDetailsCache[p]) {
359 | p--;
360 | }
361 |
362 | details = peg$posDetailsCache[p];
363 | details = {
364 | line: details.line,
365 | column: details.column
366 | };
367 |
368 | while (p < pos) {
369 | if (input.charCodeAt(p) === 10) {
370 | details.line++;
371 | details.column = 1;
372 | } else {
373 | details.column++;
374 | }
375 |
376 | p++;
377 | }
378 |
379 | peg$posDetailsCache[pos] = details;
380 | return details;
381 | }
382 | }
383 |
384 | function peg$computeLocation(startPos, endPos) {
385 | var startPosDetails = peg$computePosDetails(startPos),
386 | endPosDetails = peg$computePosDetails(endPos);
387 |
388 | return {
389 | start: {
390 | offset: startPos,
391 | line: startPosDetails.line,
392 | column: startPosDetails.column
393 | },
394 | end: {
395 | offset: endPos,
396 | line: endPosDetails.line,
397 | column: endPosDetails.column
398 | }
399 | };
400 | }
401 |
402 | function peg$fail(expected) {
403 | if (peg$currPos < peg$maxFailPos) { return; }
404 |
405 | if (peg$currPos > peg$maxFailPos) {
406 | peg$maxFailPos = peg$currPos;
407 | peg$maxFailExpected = [];
408 | }
409 |
410 | peg$maxFailExpected.push(expected);
411 | }
412 |
413 | function peg$buildSimpleError(message, location) {
414 | return new peg$SyntaxError(message, null, null, location);
415 | }
416 |
417 | function peg$buildStructuredError(expected, found, location) {
418 | return new peg$SyntaxError(
419 | peg$SyntaxError.buildMessage(expected, found),
420 | expected,
421 | found,
422 | location
423 | );
424 | }
425 |
426 | function peg$parsestart() {
427 | var s0, s1, s2, s3;
428 |
429 | var key = peg$currPos * 33 + 0,
430 | cached = peg$resultsCache[key];
431 |
432 | if (cached) {
433 | peg$currPos = cached.nextPos;
434 |
435 | return cached.result;
436 | }
437 |
438 | s0 = peg$currPos;
439 | s1 = peg$parse_();
440 | if (s1 !== peg$FAILED) {
441 | s2 = peg$parseselectors();
442 | if (s2 !== peg$FAILED) {
443 | s3 = peg$parse_();
444 | if (s3 !== peg$FAILED) {
445 | peg$savedPos = s0;
446 | s1 = peg$c0(s2);
447 | s0 = s1;
448 | } else {
449 | peg$currPos = s0;
450 | s0 = peg$FAILED;
451 | }
452 | } else {
453 | peg$currPos = s0;
454 | s0 = peg$FAILED;
455 | }
456 | } else {
457 | peg$currPos = s0;
458 | s0 = peg$FAILED;
459 | }
460 | if (s0 === peg$FAILED) {
461 | s0 = peg$currPos;
462 | s1 = peg$parse_();
463 | if (s1 !== peg$FAILED) {
464 | peg$savedPos = s0;
465 | s1 = peg$c1();
466 | }
467 | s0 = s1;
468 | }
469 |
470 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
471 |
472 | return s0;
473 | }
474 |
475 | function peg$parse_() {
476 | var s0, s1;
477 |
478 | var key = peg$currPos * 33 + 1,
479 | cached = peg$resultsCache[key];
480 |
481 | if (cached) {
482 | peg$currPos = cached.nextPos;
483 |
484 | return cached.result;
485 | }
486 |
487 | s0 = [];
488 | if (input.charCodeAt(peg$currPos) === 32) {
489 | s1 = peg$c2;
490 | peg$currPos++;
491 | } else {
492 | s1 = peg$FAILED;
493 | if (peg$silentFails === 0) { peg$fail(peg$c3); }
494 | }
495 | while (s1 !== peg$FAILED) {
496 | s0.push(s1);
497 | if (input.charCodeAt(peg$currPos) === 32) {
498 | s1 = peg$c2;
499 | peg$currPos++;
500 | } else {
501 | s1 = peg$FAILED;
502 | if (peg$silentFails === 0) { peg$fail(peg$c3); }
503 | }
504 | }
505 |
506 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
507 |
508 | return s0;
509 | }
510 |
511 | function peg$parseidentifierName() {
512 | var s0, s1, s2;
513 |
514 | var key = peg$currPos * 33 + 2,
515 | cached = peg$resultsCache[key];
516 |
517 | if (cached) {
518 | peg$currPos = cached.nextPos;
519 |
520 | return cached.result;
521 | }
522 |
523 | s0 = peg$currPos;
524 | s1 = [];
525 | if (peg$c4.test(input.charAt(peg$currPos))) {
526 | s2 = input.charAt(peg$currPos);
527 | peg$currPos++;
528 | } else {
529 | s2 = peg$FAILED;
530 | if (peg$silentFails === 0) { peg$fail(peg$c5); }
531 | }
532 | if (s2 !== peg$FAILED) {
533 | while (s2 !== peg$FAILED) {
534 | s1.push(s2);
535 | if (peg$c4.test(input.charAt(peg$currPos))) {
536 | s2 = input.charAt(peg$currPos);
537 | peg$currPos++;
538 | } else {
539 | s2 = peg$FAILED;
540 | if (peg$silentFails === 0) { peg$fail(peg$c5); }
541 | }
542 | }
543 | } else {
544 | s1 = peg$FAILED;
545 | }
546 | if (s1 !== peg$FAILED) {
547 | peg$savedPos = s0;
548 | s1 = peg$c6(s1);
549 | }
550 | s0 = s1;
551 |
552 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
553 |
554 | return s0;
555 | }
556 |
557 | function peg$parsebinaryOp() {
558 | var s0, s1, s2, s3;
559 |
560 | var key = peg$currPos * 33 + 3,
561 | cached = peg$resultsCache[key];
562 |
563 | if (cached) {
564 | peg$currPos = cached.nextPos;
565 |
566 | return cached.result;
567 | }
568 |
569 | s0 = peg$currPos;
570 | s1 = peg$parse_();
571 | if (s1 !== peg$FAILED) {
572 | if (input.charCodeAt(peg$currPos) === 62) {
573 | s2 = peg$c7;
574 | peg$currPos++;
575 | } else {
576 | s2 = peg$FAILED;
577 | if (peg$silentFails === 0) { peg$fail(peg$c8); }
578 | }
579 | if (s2 !== peg$FAILED) {
580 | s3 = peg$parse_();
581 | if (s3 !== peg$FAILED) {
582 | peg$savedPos = s0;
583 | s1 = peg$c9();
584 | s0 = s1;
585 | } else {
586 | peg$currPos = s0;
587 | s0 = peg$FAILED;
588 | }
589 | } else {
590 | peg$currPos = s0;
591 | s0 = peg$FAILED;
592 | }
593 | } else {
594 | peg$currPos = s0;
595 | s0 = peg$FAILED;
596 | }
597 | if (s0 === peg$FAILED) {
598 | s0 = peg$currPos;
599 | s1 = peg$parse_();
600 | if (s1 !== peg$FAILED) {
601 | if (input.charCodeAt(peg$currPos) === 126) {
602 | s2 = peg$c10;
603 | peg$currPos++;
604 | } else {
605 | s2 = peg$FAILED;
606 | if (peg$silentFails === 0) { peg$fail(peg$c11); }
607 | }
608 | if (s2 !== peg$FAILED) {
609 | s3 = peg$parse_();
610 | if (s3 !== peg$FAILED) {
611 | peg$savedPos = s0;
612 | s1 = peg$c12();
613 | s0 = s1;
614 | } else {
615 | peg$currPos = s0;
616 | s0 = peg$FAILED;
617 | }
618 | } else {
619 | peg$currPos = s0;
620 | s0 = peg$FAILED;
621 | }
622 | } else {
623 | peg$currPos = s0;
624 | s0 = peg$FAILED;
625 | }
626 | if (s0 === peg$FAILED) {
627 | s0 = peg$currPos;
628 | s1 = peg$parse_();
629 | if (s1 !== peg$FAILED) {
630 | if (input.charCodeAt(peg$currPos) === 43) {
631 | s2 = peg$c13;
632 | peg$currPos++;
633 | } else {
634 | s2 = peg$FAILED;
635 | if (peg$silentFails === 0) { peg$fail(peg$c14); }
636 | }
637 | if (s2 !== peg$FAILED) {
638 | s3 = peg$parse_();
639 | if (s3 !== peg$FAILED) {
640 | peg$savedPos = s0;
641 | s1 = peg$c15();
642 | s0 = s1;
643 | } else {
644 | peg$currPos = s0;
645 | s0 = peg$FAILED;
646 | }
647 | } else {
648 | peg$currPos = s0;
649 | s0 = peg$FAILED;
650 | }
651 | } else {
652 | peg$currPos = s0;
653 | s0 = peg$FAILED;
654 | }
655 | if (s0 === peg$FAILED) {
656 | s0 = peg$currPos;
657 | if (input.charCodeAt(peg$currPos) === 32) {
658 | s1 = peg$c2;
659 | peg$currPos++;
660 | } else {
661 | s1 = peg$FAILED;
662 | if (peg$silentFails === 0) { peg$fail(peg$c3); }
663 | }
664 | if (s1 !== peg$FAILED) {
665 | s2 = peg$parse_();
666 | if (s2 !== peg$FAILED) {
667 | peg$savedPos = s0;
668 | s1 = peg$c16();
669 | s0 = s1;
670 | } else {
671 | peg$currPos = s0;
672 | s0 = peg$FAILED;
673 | }
674 | } else {
675 | peg$currPos = s0;
676 | s0 = peg$FAILED;
677 | }
678 | }
679 | }
680 | }
681 |
682 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
683 |
684 | return s0;
685 | }
686 |
687 | function peg$parsehasSelectors() {
688 | var s0, s1, s2, s3, s4, s5, s6, s7;
689 |
690 | var key = peg$currPos * 33 + 4,
691 | cached = peg$resultsCache[key];
692 |
693 | if (cached) {
694 | peg$currPos = cached.nextPos;
695 |
696 | return cached.result;
697 | }
698 |
699 | s0 = peg$currPos;
700 | s1 = peg$parsehasSelector();
701 | if (s1 !== peg$FAILED) {
702 | s2 = [];
703 | s3 = peg$currPos;
704 | s4 = peg$parse_();
705 | if (s4 !== peg$FAILED) {
706 | if (input.charCodeAt(peg$currPos) === 44) {
707 | s5 = peg$c17;
708 | peg$currPos++;
709 | } else {
710 | s5 = peg$FAILED;
711 | if (peg$silentFails === 0) { peg$fail(peg$c18); }
712 | }
713 | if (s5 !== peg$FAILED) {
714 | s6 = peg$parse_();
715 | if (s6 !== peg$FAILED) {
716 | s7 = peg$parsehasSelector();
717 | if (s7 !== peg$FAILED) {
718 | s4 = [s4, s5, s6, s7];
719 | s3 = s4;
720 | } else {
721 | peg$currPos = s3;
722 | s3 = peg$FAILED;
723 | }
724 | } else {
725 | peg$currPos = s3;
726 | s3 = peg$FAILED;
727 | }
728 | } else {
729 | peg$currPos = s3;
730 | s3 = peg$FAILED;
731 | }
732 | } else {
733 | peg$currPos = s3;
734 | s3 = peg$FAILED;
735 | }
736 | while (s3 !== peg$FAILED) {
737 | s2.push(s3);
738 | s3 = peg$currPos;
739 | s4 = peg$parse_();
740 | if (s4 !== peg$FAILED) {
741 | if (input.charCodeAt(peg$currPos) === 44) {
742 | s5 = peg$c17;
743 | peg$currPos++;
744 | } else {
745 | s5 = peg$FAILED;
746 | if (peg$silentFails === 0) { peg$fail(peg$c18); }
747 | }
748 | if (s5 !== peg$FAILED) {
749 | s6 = peg$parse_();
750 | if (s6 !== peg$FAILED) {
751 | s7 = peg$parsehasSelector();
752 | if (s7 !== peg$FAILED) {
753 | s4 = [s4, s5, s6, s7];
754 | s3 = s4;
755 | } else {
756 | peg$currPos = s3;
757 | s3 = peg$FAILED;
758 | }
759 | } else {
760 | peg$currPos = s3;
761 | s3 = peg$FAILED;
762 | }
763 | } else {
764 | peg$currPos = s3;
765 | s3 = peg$FAILED;
766 | }
767 | } else {
768 | peg$currPos = s3;
769 | s3 = peg$FAILED;
770 | }
771 | }
772 | if (s2 !== peg$FAILED) {
773 | peg$savedPos = s0;
774 | s1 = peg$c19(s1, s2);
775 | s0 = s1;
776 | } else {
777 | peg$currPos = s0;
778 | s0 = peg$FAILED;
779 | }
780 | } else {
781 | peg$currPos = s0;
782 | s0 = peg$FAILED;
783 | }
784 |
785 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
786 |
787 | return s0;
788 | }
789 |
790 | function peg$parseselectors() {
791 | var s0, s1, s2, s3, s4, s5, s6, s7;
792 |
793 | var key = peg$currPos * 33 + 5,
794 | cached = peg$resultsCache[key];
795 |
796 | if (cached) {
797 | peg$currPos = cached.nextPos;
798 |
799 | return cached.result;
800 | }
801 |
802 | s0 = peg$currPos;
803 | s1 = peg$parseselector();
804 | if (s1 !== peg$FAILED) {
805 | s2 = [];
806 | s3 = peg$currPos;
807 | s4 = peg$parse_();
808 | if (s4 !== peg$FAILED) {
809 | if (input.charCodeAt(peg$currPos) === 44) {
810 | s5 = peg$c17;
811 | peg$currPos++;
812 | } else {
813 | s5 = peg$FAILED;
814 | if (peg$silentFails === 0) { peg$fail(peg$c18); }
815 | }
816 | if (s5 !== peg$FAILED) {
817 | s6 = peg$parse_();
818 | if (s6 !== peg$FAILED) {
819 | s7 = peg$parseselector();
820 | if (s7 !== peg$FAILED) {
821 | s4 = [s4, s5, s6, s7];
822 | s3 = s4;
823 | } else {
824 | peg$currPos = s3;
825 | s3 = peg$FAILED;
826 | }
827 | } else {
828 | peg$currPos = s3;
829 | s3 = peg$FAILED;
830 | }
831 | } else {
832 | peg$currPos = s3;
833 | s3 = peg$FAILED;
834 | }
835 | } else {
836 | peg$currPos = s3;
837 | s3 = peg$FAILED;
838 | }
839 | while (s3 !== peg$FAILED) {
840 | s2.push(s3);
841 | s3 = peg$currPos;
842 | s4 = peg$parse_();
843 | if (s4 !== peg$FAILED) {
844 | if (input.charCodeAt(peg$currPos) === 44) {
845 | s5 = peg$c17;
846 | peg$currPos++;
847 | } else {
848 | s5 = peg$FAILED;
849 | if (peg$silentFails === 0) { peg$fail(peg$c18); }
850 | }
851 | if (s5 !== peg$FAILED) {
852 | s6 = peg$parse_();
853 | if (s6 !== peg$FAILED) {
854 | s7 = peg$parseselector();
855 | if (s7 !== peg$FAILED) {
856 | s4 = [s4, s5, s6, s7];
857 | s3 = s4;
858 | } else {
859 | peg$currPos = s3;
860 | s3 = peg$FAILED;
861 | }
862 | } else {
863 | peg$currPos = s3;
864 | s3 = peg$FAILED;
865 | }
866 | } else {
867 | peg$currPos = s3;
868 | s3 = peg$FAILED;
869 | }
870 | } else {
871 | peg$currPos = s3;
872 | s3 = peg$FAILED;
873 | }
874 | }
875 | if (s2 !== peg$FAILED) {
876 | peg$savedPos = s0;
877 | s1 = peg$c19(s1, s2);
878 | s0 = s1;
879 | } else {
880 | peg$currPos = s0;
881 | s0 = peg$FAILED;
882 | }
883 | } else {
884 | peg$currPos = s0;
885 | s0 = peg$FAILED;
886 | }
887 |
888 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
889 |
890 | return s0;
891 | }
892 |
893 | function peg$parsehasSelector() {
894 | var s0, s1, s2;
895 |
896 | var key = peg$currPos * 33 + 6,
897 | cached = peg$resultsCache[key];
898 |
899 | if (cached) {
900 | peg$currPos = cached.nextPos;
901 |
902 | return cached.result;
903 | }
904 |
905 | s0 = peg$currPos;
906 | s1 = peg$parsebinaryOp();
907 | if (s1 === peg$FAILED) {
908 | s1 = null;
909 | }
910 | if (s1 !== peg$FAILED) {
911 | s2 = peg$parseselector();
912 | if (s2 !== peg$FAILED) {
913 | peg$savedPos = s0;
914 | s1 = peg$c20(s1, s2);
915 | s0 = s1;
916 | } else {
917 | peg$currPos = s0;
918 | s0 = peg$FAILED;
919 | }
920 | } else {
921 | peg$currPos = s0;
922 | s0 = peg$FAILED;
923 | }
924 |
925 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
926 |
927 | return s0;
928 | }
929 |
930 | function peg$parseselector() {
931 | var s0, s1, s2, s3, s4, s5;
932 |
933 | var key = peg$currPos * 33 + 7,
934 | cached = peg$resultsCache[key];
935 |
936 | if (cached) {
937 | peg$currPos = cached.nextPos;
938 |
939 | return cached.result;
940 | }
941 |
942 | s0 = peg$currPos;
943 | s1 = peg$parsesequence();
944 | if (s1 !== peg$FAILED) {
945 | s2 = [];
946 | s3 = peg$currPos;
947 | s4 = peg$parsebinaryOp();
948 | if (s4 !== peg$FAILED) {
949 | s5 = peg$parsesequence();
950 | if (s5 !== peg$FAILED) {
951 | s4 = [s4, s5];
952 | s3 = s4;
953 | } else {
954 | peg$currPos = s3;
955 | s3 = peg$FAILED;
956 | }
957 | } else {
958 | peg$currPos = s3;
959 | s3 = peg$FAILED;
960 | }
961 | while (s3 !== peg$FAILED) {
962 | s2.push(s3);
963 | s3 = peg$currPos;
964 | s4 = peg$parsebinaryOp();
965 | if (s4 !== peg$FAILED) {
966 | s5 = peg$parsesequence();
967 | if (s5 !== peg$FAILED) {
968 | s4 = [s4, s5];
969 | s3 = s4;
970 | } else {
971 | peg$currPos = s3;
972 | s3 = peg$FAILED;
973 | }
974 | } else {
975 | peg$currPos = s3;
976 | s3 = peg$FAILED;
977 | }
978 | }
979 | if (s2 !== peg$FAILED) {
980 | peg$savedPos = s0;
981 | s1 = peg$c21(s1, s2);
982 | s0 = s1;
983 | } else {
984 | peg$currPos = s0;
985 | s0 = peg$FAILED;
986 | }
987 | } else {
988 | peg$currPos = s0;
989 | s0 = peg$FAILED;
990 | }
991 |
992 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
993 |
994 | return s0;
995 | }
996 |
997 | function peg$parsesequence() {
998 | var s0, s1, s2, s3;
999 |
1000 | var key = peg$currPos * 33 + 8,
1001 | cached = peg$resultsCache[key];
1002 |
1003 | if (cached) {
1004 | peg$currPos = cached.nextPos;
1005 |
1006 | return cached.result;
1007 | }
1008 |
1009 | s0 = peg$currPos;
1010 | if (input.charCodeAt(peg$currPos) === 33) {
1011 | s1 = peg$c22;
1012 | peg$currPos++;
1013 | } else {
1014 | s1 = peg$FAILED;
1015 | if (peg$silentFails === 0) { peg$fail(peg$c23); }
1016 | }
1017 | if (s1 === peg$FAILED) {
1018 | s1 = null;
1019 | }
1020 | if (s1 !== peg$FAILED) {
1021 | s2 = [];
1022 | s3 = peg$parseatom();
1023 | if (s3 !== peg$FAILED) {
1024 | while (s3 !== peg$FAILED) {
1025 | s2.push(s3);
1026 | s3 = peg$parseatom();
1027 | }
1028 | } else {
1029 | s2 = peg$FAILED;
1030 | }
1031 | if (s2 !== peg$FAILED) {
1032 | peg$savedPos = s0;
1033 | s1 = peg$c24(s1, s2);
1034 | s0 = s1;
1035 | } else {
1036 | peg$currPos = s0;
1037 | s0 = peg$FAILED;
1038 | }
1039 | } else {
1040 | peg$currPos = s0;
1041 | s0 = peg$FAILED;
1042 | }
1043 |
1044 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1045 |
1046 | return s0;
1047 | }
1048 |
1049 | function peg$parseatom() {
1050 | var s0;
1051 |
1052 | var key = peg$currPos * 33 + 9,
1053 | cached = peg$resultsCache[key];
1054 |
1055 | if (cached) {
1056 | peg$currPos = cached.nextPos;
1057 |
1058 | return cached.result;
1059 | }
1060 |
1061 | s0 = peg$parsewildcard();
1062 | if (s0 === peg$FAILED) {
1063 | s0 = peg$parseidentifier();
1064 | if (s0 === peg$FAILED) {
1065 | s0 = peg$parseattr();
1066 | if (s0 === peg$FAILED) {
1067 | s0 = peg$parsefield();
1068 | if (s0 === peg$FAILED) {
1069 | s0 = peg$parsenegation();
1070 | if (s0 === peg$FAILED) {
1071 | s0 = peg$parsematches();
1072 | if (s0 === peg$FAILED) {
1073 | s0 = peg$parseis();
1074 | if (s0 === peg$FAILED) {
1075 | s0 = peg$parsehas();
1076 | if (s0 === peg$FAILED) {
1077 | s0 = peg$parsefirstChild();
1078 | if (s0 === peg$FAILED) {
1079 | s0 = peg$parselastChild();
1080 | if (s0 === peg$FAILED) {
1081 | s0 = peg$parsenthChild();
1082 | if (s0 === peg$FAILED) {
1083 | s0 = peg$parsenthLastChild();
1084 | if (s0 === peg$FAILED) {
1085 | s0 = peg$parseclass();
1086 | }
1087 | }
1088 | }
1089 | }
1090 | }
1091 | }
1092 | }
1093 | }
1094 | }
1095 | }
1096 | }
1097 | }
1098 |
1099 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1100 |
1101 | return s0;
1102 | }
1103 |
1104 | function peg$parsewildcard() {
1105 | var s0, s1;
1106 |
1107 | var key = peg$currPos * 33 + 10,
1108 | cached = peg$resultsCache[key];
1109 |
1110 | if (cached) {
1111 | peg$currPos = cached.nextPos;
1112 |
1113 | return cached.result;
1114 | }
1115 |
1116 | s0 = peg$currPos;
1117 | if (input.charCodeAt(peg$currPos) === 42) {
1118 | s1 = peg$c25;
1119 | peg$currPos++;
1120 | } else {
1121 | s1 = peg$FAILED;
1122 | if (peg$silentFails === 0) { peg$fail(peg$c26); }
1123 | }
1124 | if (s1 !== peg$FAILED) {
1125 | peg$savedPos = s0;
1126 | s1 = peg$c27(s1);
1127 | }
1128 | s0 = s1;
1129 |
1130 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1131 |
1132 | return s0;
1133 | }
1134 |
1135 | function peg$parseidentifier() {
1136 | var s0, s1, s2;
1137 |
1138 | var key = peg$currPos * 33 + 11,
1139 | cached = peg$resultsCache[key];
1140 |
1141 | if (cached) {
1142 | peg$currPos = cached.nextPos;
1143 |
1144 | return cached.result;
1145 | }
1146 |
1147 | s0 = peg$currPos;
1148 | if (input.charCodeAt(peg$currPos) === 35) {
1149 | s1 = peg$c28;
1150 | peg$currPos++;
1151 | } else {
1152 | s1 = peg$FAILED;
1153 | if (peg$silentFails === 0) { peg$fail(peg$c29); }
1154 | }
1155 | if (s1 === peg$FAILED) {
1156 | s1 = null;
1157 | }
1158 | if (s1 !== peg$FAILED) {
1159 | s2 = peg$parseidentifierName();
1160 | if (s2 !== peg$FAILED) {
1161 | peg$savedPos = s0;
1162 | s1 = peg$c30(s2);
1163 | s0 = s1;
1164 | } else {
1165 | peg$currPos = s0;
1166 | s0 = peg$FAILED;
1167 | }
1168 | } else {
1169 | peg$currPos = s0;
1170 | s0 = peg$FAILED;
1171 | }
1172 |
1173 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1174 |
1175 | return s0;
1176 | }
1177 |
1178 | function peg$parseattr() {
1179 | var s0, s1, s2, s3, s4, s5;
1180 |
1181 | var key = peg$currPos * 33 + 12,
1182 | cached = peg$resultsCache[key];
1183 |
1184 | if (cached) {
1185 | peg$currPos = cached.nextPos;
1186 |
1187 | return cached.result;
1188 | }
1189 |
1190 | s0 = peg$currPos;
1191 | if (input.charCodeAt(peg$currPos) === 91) {
1192 | s1 = peg$c31;
1193 | peg$currPos++;
1194 | } else {
1195 | s1 = peg$FAILED;
1196 | if (peg$silentFails === 0) { peg$fail(peg$c32); }
1197 | }
1198 | if (s1 !== peg$FAILED) {
1199 | s2 = peg$parse_();
1200 | if (s2 !== peg$FAILED) {
1201 | s3 = peg$parseattrValue();
1202 | if (s3 !== peg$FAILED) {
1203 | s4 = peg$parse_();
1204 | if (s4 !== peg$FAILED) {
1205 | if (input.charCodeAt(peg$currPos) === 93) {
1206 | s5 = peg$c33;
1207 | peg$currPos++;
1208 | } else {
1209 | s5 = peg$FAILED;
1210 | if (peg$silentFails === 0) { peg$fail(peg$c34); }
1211 | }
1212 | if (s5 !== peg$FAILED) {
1213 | peg$savedPos = s0;
1214 | s1 = peg$c35(s3);
1215 | s0 = s1;
1216 | } else {
1217 | peg$currPos = s0;
1218 | s0 = peg$FAILED;
1219 | }
1220 | } else {
1221 | peg$currPos = s0;
1222 | s0 = peg$FAILED;
1223 | }
1224 | } else {
1225 | peg$currPos = s0;
1226 | s0 = peg$FAILED;
1227 | }
1228 | } else {
1229 | peg$currPos = s0;
1230 | s0 = peg$FAILED;
1231 | }
1232 | } else {
1233 | peg$currPos = s0;
1234 | s0 = peg$FAILED;
1235 | }
1236 |
1237 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1238 |
1239 | return s0;
1240 | }
1241 |
1242 | function peg$parseattrOps() {
1243 | var s0, s1, s2;
1244 |
1245 | var key = peg$currPos * 33 + 13,
1246 | cached = peg$resultsCache[key];
1247 |
1248 | if (cached) {
1249 | peg$currPos = cached.nextPos;
1250 |
1251 | return cached.result;
1252 | }
1253 |
1254 | s0 = peg$currPos;
1255 | if (peg$c36.test(input.charAt(peg$currPos))) {
1256 | s1 = input.charAt(peg$currPos);
1257 | peg$currPos++;
1258 | } else {
1259 | s1 = peg$FAILED;
1260 | if (peg$silentFails === 0) { peg$fail(peg$c37); }
1261 | }
1262 | if (s1 === peg$FAILED) {
1263 | s1 = null;
1264 | }
1265 | if (s1 !== peg$FAILED) {
1266 | if (input.charCodeAt(peg$currPos) === 61) {
1267 | s2 = peg$c38;
1268 | peg$currPos++;
1269 | } else {
1270 | s2 = peg$FAILED;
1271 | if (peg$silentFails === 0) { peg$fail(peg$c39); }
1272 | }
1273 | if (s2 !== peg$FAILED) {
1274 | peg$savedPos = s0;
1275 | s1 = peg$c40(s1);
1276 | s0 = s1;
1277 | } else {
1278 | peg$currPos = s0;
1279 | s0 = peg$FAILED;
1280 | }
1281 | } else {
1282 | peg$currPos = s0;
1283 | s0 = peg$FAILED;
1284 | }
1285 | if (s0 === peg$FAILED) {
1286 | if (peg$c41.test(input.charAt(peg$currPos))) {
1287 | s0 = input.charAt(peg$currPos);
1288 | peg$currPos++;
1289 | } else {
1290 | s0 = peg$FAILED;
1291 | if (peg$silentFails === 0) { peg$fail(peg$c42); }
1292 | }
1293 | }
1294 |
1295 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1296 |
1297 | return s0;
1298 | }
1299 |
1300 | function peg$parseattrEqOps() {
1301 | var s0, s1, s2;
1302 |
1303 | var key = peg$currPos * 33 + 14,
1304 | cached = peg$resultsCache[key];
1305 |
1306 | if (cached) {
1307 | peg$currPos = cached.nextPos;
1308 |
1309 | return cached.result;
1310 | }
1311 |
1312 | s0 = peg$currPos;
1313 | if (input.charCodeAt(peg$currPos) === 33) {
1314 | s1 = peg$c22;
1315 | peg$currPos++;
1316 | } else {
1317 | s1 = peg$FAILED;
1318 | if (peg$silentFails === 0) { peg$fail(peg$c23); }
1319 | }
1320 | if (s1 === peg$FAILED) {
1321 | s1 = null;
1322 | }
1323 | if (s1 !== peg$FAILED) {
1324 | if (input.charCodeAt(peg$currPos) === 61) {
1325 | s2 = peg$c38;
1326 | peg$currPos++;
1327 | } else {
1328 | s2 = peg$FAILED;
1329 | if (peg$silentFails === 0) { peg$fail(peg$c39); }
1330 | }
1331 | if (s2 !== peg$FAILED) {
1332 | peg$savedPos = s0;
1333 | s1 = peg$c40(s1);
1334 | s0 = s1;
1335 | } else {
1336 | peg$currPos = s0;
1337 | s0 = peg$FAILED;
1338 | }
1339 | } else {
1340 | peg$currPos = s0;
1341 | s0 = peg$FAILED;
1342 | }
1343 |
1344 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1345 |
1346 | return s0;
1347 | }
1348 |
1349 | function peg$parseattrName() {
1350 | var s0, s1, s2, s3, s4, s5;
1351 |
1352 | var key = peg$currPos * 33 + 15,
1353 | cached = peg$resultsCache[key];
1354 |
1355 | if (cached) {
1356 | peg$currPos = cached.nextPos;
1357 |
1358 | return cached.result;
1359 | }
1360 |
1361 | s0 = peg$currPos;
1362 | s1 = peg$parseidentifierName();
1363 | if (s1 !== peg$FAILED) {
1364 | s2 = [];
1365 | s3 = peg$currPos;
1366 | if (input.charCodeAt(peg$currPos) === 46) {
1367 | s4 = peg$c43;
1368 | peg$currPos++;
1369 | } else {
1370 | s4 = peg$FAILED;
1371 | if (peg$silentFails === 0) { peg$fail(peg$c44); }
1372 | }
1373 | if (s4 !== peg$FAILED) {
1374 | s5 = peg$parseidentifierName();
1375 | if (s5 !== peg$FAILED) {
1376 | s4 = [s4, s5];
1377 | s3 = s4;
1378 | } else {
1379 | peg$currPos = s3;
1380 | s3 = peg$FAILED;
1381 | }
1382 | } else {
1383 | peg$currPos = s3;
1384 | s3 = peg$FAILED;
1385 | }
1386 | while (s3 !== peg$FAILED) {
1387 | s2.push(s3);
1388 | s3 = peg$currPos;
1389 | if (input.charCodeAt(peg$currPos) === 46) {
1390 | s4 = peg$c43;
1391 | peg$currPos++;
1392 | } else {
1393 | s4 = peg$FAILED;
1394 | if (peg$silentFails === 0) { peg$fail(peg$c44); }
1395 | }
1396 | if (s4 !== peg$FAILED) {
1397 | s5 = peg$parseidentifierName();
1398 | if (s5 !== peg$FAILED) {
1399 | s4 = [s4, s5];
1400 | s3 = s4;
1401 | } else {
1402 | peg$currPos = s3;
1403 | s3 = peg$FAILED;
1404 | }
1405 | } else {
1406 | peg$currPos = s3;
1407 | s3 = peg$FAILED;
1408 | }
1409 | }
1410 | if (s2 !== peg$FAILED) {
1411 | peg$savedPos = s0;
1412 | s1 = peg$c45(s1, s2);
1413 | s0 = s1;
1414 | } else {
1415 | peg$currPos = s0;
1416 | s0 = peg$FAILED;
1417 | }
1418 | } else {
1419 | peg$currPos = s0;
1420 | s0 = peg$FAILED;
1421 | }
1422 |
1423 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1424 |
1425 | return s0;
1426 | }
1427 |
1428 | function peg$parseattrValue() {
1429 | var s0, s1, s2, s3, s4, s5;
1430 |
1431 | var key = peg$currPos * 33 + 16,
1432 | cached = peg$resultsCache[key];
1433 |
1434 | if (cached) {
1435 | peg$currPos = cached.nextPos;
1436 |
1437 | return cached.result;
1438 | }
1439 |
1440 | s0 = peg$currPos;
1441 | s1 = peg$parseattrName();
1442 | if (s1 !== peg$FAILED) {
1443 | s2 = peg$parse_();
1444 | if (s2 !== peg$FAILED) {
1445 | s3 = peg$parseattrEqOps();
1446 | if (s3 !== peg$FAILED) {
1447 | s4 = peg$parse_();
1448 | if (s4 !== peg$FAILED) {
1449 | s5 = peg$parsetype();
1450 | if (s5 === peg$FAILED) {
1451 | s5 = peg$parseregex();
1452 | }
1453 | if (s5 !== peg$FAILED) {
1454 | peg$savedPos = s0;
1455 | s1 = peg$c46(s1, s3, s5);
1456 | s0 = s1;
1457 | } else {
1458 | peg$currPos = s0;
1459 | s0 = peg$FAILED;
1460 | }
1461 | } else {
1462 | peg$currPos = s0;
1463 | s0 = peg$FAILED;
1464 | }
1465 | } else {
1466 | peg$currPos = s0;
1467 | s0 = peg$FAILED;
1468 | }
1469 | } else {
1470 | peg$currPos = s0;
1471 | s0 = peg$FAILED;
1472 | }
1473 | } else {
1474 | peg$currPos = s0;
1475 | s0 = peg$FAILED;
1476 | }
1477 | if (s0 === peg$FAILED) {
1478 | s0 = peg$currPos;
1479 | s1 = peg$parseattrName();
1480 | if (s1 !== peg$FAILED) {
1481 | s2 = peg$parse_();
1482 | if (s2 !== peg$FAILED) {
1483 | s3 = peg$parseattrOps();
1484 | if (s3 !== peg$FAILED) {
1485 | s4 = peg$parse_();
1486 | if (s4 !== peg$FAILED) {
1487 | s5 = peg$parsestring();
1488 | if (s5 === peg$FAILED) {
1489 | s5 = peg$parsenumber();
1490 | if (s5 === peg$FAILED) {
1491 | s5 = peg$parsepath();
1492 | }
1493 | }
1494 | if (s5 !== peg$FAILED) {
1495 | peg$savedPos = s0;
1496 | s1 = peg$c46(s1, s3, s5);
1497 | s0 = s1;
1498 | } else {
1499 | peg$currPos = s0;
1500 | s0 = peg$FAILED;
1501 | }
1502 | } else {
1503 | peg$currPos = s0;
1504 | s0 = peg$FAILED;
1505 | }
1506 | } else {
1507 | peg$currPos = s0;
1508 | s0 = peg$FAILED;
1509 | }
1510 | } else {
1511 | peg$currPos = s0;
1512 | s0 = peg$FAILED;
1513 | }
1514 | } else {
1515 | peg$currPos = s0;
1516 | s0 = peg$FAILED;
1517 | }
1518 | if (s0 === peg$FAILED) {
1519 | s0 = peg$currPos;
1520 | s1 = peg$parseattrName();
1521 | if (s1 !== peg$FAILED) {
1522 | peg$savedPos = s0;
1523 | s1 = peg$c47(s1);
1524 | }
1525 | s0 = s1;
1526 | }
1527 | }
1528 |
1529 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1530 |
1531 | return s0;
1532 | }
1533 |
1534 | function peg$parsestring() {
1535 | var s0, s1, s2, s3, s4, s5;
1536 |
1537 | var key = peg$currPos * 33 + 17,
1538 | cached = peg$resultsCache[key];
1539 |
1540 | if (cached) {
1541 | peg$currPos = cached.nextPos;
1542 |
1543 | return cached.result;
1544 | }
1545 |
1546 | s0 = peg$currPos;
1547 | if (input.charCodeAt(peg$currPos) === 34) {
1548 | s1 = peg$c48;
1549 | peg$currPos++;
1550 | } else {
1551 | s1 = peg$FAILED;
1552 | if (peg$silentFails === 0) { peg$fail(peg$c49); }
1553 | }
1554 | if (s1 !== peg$FAILED) {
1555 | s2 = [];
1556 | if (peg$c50.test(input.charAt(peg$currPos))) {
1557 | s3 = input.charAt(peg$currPos);
1558 | peg$currPos++;
1559 | } else {
1560 | s3 = peg$FAILED;
1561 | if (peg$silentFails === 0) { peg$fail(peg$c51); }
1562 | }
1563 | if (s3 === peg$FAILED) {
1564 | s3 = peg$currPos;
1565 | if (input.charCodeAt(peg$currPos) === 92) {
1566 | s4 = peg$c52;
1567 | peg$currPos++;
1568 | } else {
1569 | s4 = peg$FAILED;
1570 | if (peg$silentFails === 0) { peg$fail(peg$c53); }
1571 | }
1572 | if (s4 !== peg$FAILED) {
1573 | if (input.length > peg$currPos) {
1574 | s5 = input.charAt(peg$currPos);
1575 | peg$currPos++;
1576 | } else {
1577 | s5 = peg$FAILED;
1578 | if (peg$silentFails === 0) { peg$fail(peg$c54); }
1579 | }
1580 | if (s5 !== peg$FAILED) {
1581 | peg$savedPos = s3;
1582 | s4 = peg$c55(s4, s5);
1583 | s3 = s4;
1584 | } else {
1585 | peg$currPos = s3;
1586 | s3 = peg$FAILED;
1587 | }
1588 | } else {
1589 | peg$currPos = s3;
1590 | s3 = peg$FAILED;
1591 | }
1592 | }
1593 | while (s3 !== peg$FAILED) {
1594 | s2.push(s3);
1595 | if (peg$c50.test(input.charAt(peg$currPos))) {
1596 | s3 = input.charAt(peg$currPos);
1597 | peg$currPos++;
1598 | } else {
1599 | s3 = peg$FAILED;
1600 | if (peg$silentFails === 0) { peg$fail(peg$c51); }
1601 | }
1602 | if (s3 === peg$FAILED) {
1603 | s3 = peg$currPos;
1604 | if (input.charCodeAt(peg$currPos) === 92) {
1605 | s4 = peg$c52;
1606 | peg$currPos++;
1607 | } else {
1608 | s4 = peg$FAILED;
1609 | if (peg$silentFails === 0) { peg$fail(peg$c53); }
1610 | }
1611 | if (s4 !== peg$FAILED) {
1612 | if (input.length > peg$currPos) {
1613 | s5 = input.charAt(peg$currPos);
1614 | peg$currPos++;
1615 | } else {
1616 | s5 = peg$FAILED;
1617 | if (peg$silentFails === 0) { peg$fail(peg$c54); }
1618 | }
1619 | if (s5 !== peg$FAILED) {
1620 | peg$savedPos = s3;
1621 | s4 = peg$c55(s4, s5);
1622 | s3 = s4;
1623 | } else {
1624 | peg$currPos = s3;
1625 | s3 = peg$FAILED;
1626 | }
1627 | } else {
1628 | peg$currPos = s3;
1629 | s3 = peg$FAILED;
1630 | }
1631 | }
1632 | }
1633 | if (s2 !== peg$FAILED) {
1634 | if (input.charCodeAt(peg$currPos) === 34) {
1635 | s3 = peg$c48;
1636 | peg$currPos++;
1637 | } else {
1638 | s3 = peg$FAILED;
1639 | if (peg$silentFails === 0) { peg$fail(peg$c49); }
1640 | }
1641 | if (s3 !== peg$FAILED) {
1642 | peg$savedPos = s0;
1643 | s1 = peg$c56(s2);
1644 | s0 = s1;
1645 | } else {
1646 | peg$currPos = s0;
1647 | s0 = peg$FAILED;
1648 | }
1649 | } else {
1650 | peg$currPos = s0;
1651 | s0 = peg$FAILED;
1652 | }
1653 | } else {
1654 | peg$currPos = s0;
1655 | s0 = peg$FAILED;
1656 | }
1657 | if (s0 === peg$FAILED) {
1658 | s0 = peg$currPos;
1659 | if (input.charCodeAt(peg$currPos) === 39) {
1660 | s1 = peg$c57;
1661 | peg$currPos++;
1662 | } else {
1663 | s1 = peg$FAILED;
1664 | if (peg$silentFails === 0) { peg$fail(peg$c58); }
1665 | }
1666 | if (s1 !== peg$FAILED) {
1667 | s2 = [];
1668 | if (peg$c59.test(input.charAt(peg$currPos))) {
1669 | s3 = input.charAt(peg$currPos);
1670 | peg$currPos++;
1671 | } else {
1672 | s3 = peg$FAILED;
1673 | if (peg$silentFails === 0) { peg$fail(peg$c60); }
1674 | }
1675 | if (s3 === peg$FAILED) {
1676 | s3 = peg$currPos;
1677 | if (input.charCodeAt(peg$currPos) === 92) {
1678 | s4 = peg$c52;
1679 | peg$currPos++;
1680 | } else {
1681 | s4 = peg$FAILED;
1682 | if (peg$silentFails === 0) { peg$fail(peg$c53); }
1683 | }
1684 | if (s4 !== peg$FAILED) {
1685 | if (input.length > peg$currPos) {
1686 | s5 = input.charAt(peg$currPos);
1687 | peg$currPos++;
1688 | } else {
1689 | s5 = peg$FAILED;
1690 | if (peg$silentFails === 0) { peg$fail(peg$c54); }
1691 | }
1692 | if (s5 !== peg$FAILED) {
1693 | peg$savedPos = s3;
1694 | s4 = peg$c55(s4, s5);
1695 | s3 = s4;
1696 | } else {
1697 | peg$currPos = s3;
1698 | s3 = peg$FAILED;
1699 | }
1700 | } else {
1701 | peg$currPos = s3;
1702 | s3 = peg$FAILED;
1703 | }
1704 | }
1705 | while (s3 !== peg$FAILED) {
1706 | s2.push(s3);
1707 | if (peg$c59.test(input.charAt(peg$currPos))) {
1708 | s3 = input.charAt(peg$currPos);
1709 | peg$currPos++;
1710 | } else {
1711 | s3 = peg$FAILED;
1712 | if (peg$silentFails === 0) { peg$fail(peg$c60); }
1713 | }
1714 | if (s3 === peg$FAILED) {
1715 | s3 = peg$currPos;
1716 | if (input.charCodeAt(peg$currPos) === 92) {
1717 | s4 = peg$c52;
1718 | peg$currPos++;
1719 | } else {
1720 | s4 = peg$FAILED;
1721 | if (peg$silentFails === 0) { peg$fail(peg$c53); }
1722 | }
1723 | if (s4 !== peg$FAILED) {
1724 | if (input.length > peg$currPos) {
1725 | s5 = input.charAt(peg$currPos);
1726 | peg$currPos++;
1727 | } else {
1728 | s5 = peg$FAILED;
1729 | if (peg$silentFails === 0) { peg$fail(peg$c54); }
1730 | }
1731 | if (s5 !== peg$FAILED) {
1732 | peg$savedPos = s3;
1733 | s4 = peg$c55(s4, s5);
1734 | s3 = s4;
1735 | } else {
1736 | peg$currPos = s3;
1737 | s3 = peg$FAILED;
1738 | }
1739 | } else {
1740 | peg$currPos = s3;
1741 | s3 = peg$FAILED;
1742 | }
1743 | }
1744 | }
1745 | if (s2 !== peg$FAILED) {
1746 | if (input.charCodeAt(peg$currPos) === 39) {
1747 | s3 = peg$c57;
1748 | peg$currPos++;
1749 | } else {
1750 | s3 = peg$FAILED;
1751 | if (peg$silentFails === 0) { peg$fail(peg$c58); }
1752 | }
1753 | if (s3 !== peg$FAILED) {
1754 | peg$savedPos = s0;
1755 | s1 = peg$c56(s2);
1756 | s0 = s1;
1757 | } else {
1758 | peg$currPos = s0;
1759 | s0 = peg$FAILED;
1760 | }
1761 | } else {
1762 | peg$currPos = s0;
1763 | s0 = peg$FAILED;
1764 | }
1765 | } else {
1766 | peg$currPos = s0;
1767 | s0 = peg$FAILED;
1768 | }
1769 | }
1770 |
1771 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1772 |
1773 | return s0;
1774 | }
1775 |
1776 | function peg$parsenumber() {
1777 | var s0, s1, s2, s3;
1778 |
1779 | var key = peg$currPos * 33 + 18,
1780 | cached = peg$resultsCache[key];
1781 |
1782 | if (cached) {
1783 | peg$currPos = cached.nextPos;
1784 |
1785 | return cached.result;
1786 | }
1787 |
1788 | s0 = peg$currPos;
1789 | s1 = peg$currPos;
1790 | s2 = [];
1791 | if (peg$c61.test(input.charAt(peg$currPos))) {
1792 | s3 = input.charAt(peg$currPos);
1793 | peg$currPos++;
1794 | } else {
1795 | s3 = peg$FAILED;
1796 | if (peg$silentFails === 0) { peg$fail(peg$c62); }
1797 | }
1798 | while (s3 !== peg$FAILED) {
1799 | s2.push(s3);
1800 | if (peg$c61.test(input.charAt(peg$currPos))) {
1801 | s3 = input.charAt(peg$currPos);
1802 | peg$currPos++;
1803 | } else {
1804 | s3 = peg$FAILED;
1805 | if (peg$silentFails === 0) { peg$fail(peg$c62); }
1806 | }
1807 | }
1808 | if (s2 !== peg$FAILED) {
1809 | if (input.charCodeAt(peg$currPos) === 46) {
1810 | s3 = peg$c43;
1811 | peg$currPos++;
1812 | } else {
1813 | s3 = peg$FAILED;
1814 | if (peg$silentFails === 0) { peg$fail(peg$c44); }
1815 | }
1816 | if (s3 !== peg$FAILED) {
1817 | s2 = [s2, s3];
1818 | s1 = s2;
1819 | } else {
1820 | peg$currPos = s1;
1821 | s1 = peg$FAILED;
1822 | }
1823 | } else {
1824 | peg$currPos = s1;
1825 | s1 = peg$FAILED;
1826 | }
1827 | if (s1 === peg$FAILED) {
1828 | s1 = null;
1829 | }
1830 | if (s1 !== peg$FAILED) {
1831 | s2 = [];
1832 | if (peg$c61.test(input.charAt(peg$currPos))) {
1833 | s3 = input.charAt(peg$currPos);
1834 | peg$currPos++;
1835 | } else {
1836 | s3 = peg$FAILED;
1837 | if (peg$silentFails === 0) { peg$fail(peg$c62); }
1838 | }
1839 | if (s3 !== peg$FAILED) {
1840 | while (s3 !== peg$FAILED) {
1841 | s2.push(s3);
1842 | if (peg$c61.test(input.charAt(peg$currPos))) {
1843 | s3 = input.charAt(peg$currPos);
1844 | peg$currPos++;
1845 | } else {
1846 | s3 = peg$FAILED;
1847 | if (peg$silentFails === 0) { peg$fail(peg$c62); }
1848 | }
1849 | }
1850 | } else {
1851 | s2 = peg$FAILED;
1852 | }
1853 | if (s2 !== peg$FAILED) {
1854 | peg$savedPos = s0;
1855 | s1 = peg$c63(s1, s2);
1856 | s0 = s1;
1857 | } else {
1858 | peg$currPos = s0;
1859 | s0 = peg$FAILED;
1860 | }
1861 | } else {
1862 | peg$currPos = s0;
1863 | s0 = peg$FAILED;
1864 | }
1865 |
1866 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1867 |
1868 | return s0;
1869 | }
1870 |
1871 | function peg$parsepath() {
1872 | var s0, s1;
1873 |
1874 | var key = peg$currPos * 33 + 19,
1875 | cached = peg$resultsCache[key];
1876 |
1877 | if (cached) {
1878 | peg$currPos = cached.nextPos;
1879 |
1880 | return cached.result;
1881 | }
1882 |
1883 | s0 = peg$currPos;
1884 | s1 = peg$parseidentifierName();
1885 | if (s1 !== peg$FAILED) {
1886 | peg$savedPos = s0;
1887 | s1 = peg$c64(s1);
1888 | }
1889 | s0 = s1;
1890 |
1891 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1892 |
1893 | return s0;
1894 | }
1895 |
1896 | function peg$parsetype() {
1897 | var s0, s1, s2, s3, s4, s5;
1898 |
1899 | var key = peg$currPos * 33 + 20,
1900 | cached = peg$resultsCache[key];
1901 |
1902 | if (cached) {
1903 | peg$currPos = cached.nextPos;
1904 |
1905 | return cached.result;
1906 | }
1907 |
1908 | s0 = peg$currPos;
1909 | if (input.substr(peg$currPos, 5) === peg$c65) {
1910 | s1 = peg$c65;
1911 | peg$currPos += 5;
1912 | } else {
1913 | s1 = peg$FAILED;
1914 | if (peg$silentFails === 0) { peg$fail(peg$c66); }
1915 | }
1916 | if (s1 !== peg$FAILED) {
1917 | s2 = peg$parse_();
1918 | if (s2 !== peg$FAILED) {
1919 | s3 = [];
1920 | if (peg$c67.test(input.charAt(peg$currPos))) {
1921 | s4 = input.charAt(peg$currPos);
1922 | peg$currPos++;
1923 | } else {
1924 | s4 = peg$FAILED;
1925 | if (peg$silentFails === 0) { peg$fail(peg$c68); }
1926 | }
1927 | if (s4 !== peg$FAILED) {
1928 | while (s4 !== peg$FAILED) {
1929 | s3.push(s4);
1930 | if (peg$c67.test(input.charAt(peg$currPos))) {
1931 | s4 = input.charAt(peg$currPos);
1932 | peg$currPos++;
1933 | } else {
1934 | s4 = peg$FAILED;
1935 | if (peg$silentFails === 0) { peg$fail(peg$c68); }
1936 | }
1937 | }
1938 | } else {
1939 | s3 = peg$FAILED;
1940 | }
1941 | if (s3 !== peg$FAILED) {
1942 | s4 = peg$parse_();
1943 | if (s4 !== peg$FAILED) {
1944 | if (input.charCodeAt(peg$currPos) === 41) {
1945 | s5 = peg$c69;
1946 | peg$currPos++;
1947 | } else {
1948 | s5 = peg$FAILED;
1949 | if (peg$silentFails === 0) { peg$fail(peg$c70); }
1950 | }
1951 | if (s5 !== peg$FAILED) {
1952 | peg$savedPos = s0;
1953 | s1 = peg$c71(s3);
1954 | s0 = s1;
1955 | } else {
1956 | peg$currPos = s0;
1957 | s0 = peg$FAILED;
1958 | }
1959 | } else {
1960 | peg$currPos = s0;
1961 | s0 = peg$FAILED;
1962 | }
1963 | } else {
1964 | peg$currPos = s0;
1965 | s0 = peg$FAILED;
1966 | }
1967 | } else {
1968 | peg$currPos = s0;
1969 | s0 = peg$FAILED;
1970 | }
1971 | } else {
1972 | peg$currPos = s0;
1973 | s0 = peg$FAILED;
1974 | }
1975 |
1976 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1977 |
1978 | return s0;
1979 | }
1980 |
1981 | function peg$parseflags() {
1982 | var s0, s1;
1983 |
1984 | var key = peg$currPos * 33 + 21,
1985 | cached = peg$resultsCache[key];
1986 |
1987 | if (cached) {
1988 | peg$currPos = cached.nextPos;
1989 |
1990 | return cached.result;
1991 | }
1992 |
1993 | s0 = [];
1994 | if (peg$c72.test(input.charAt(peg$currPos))) {
1995 | s1 = input.charAt(peg$currPos);
1996 | peg$currPos++;
1997 | } else {
1998 | s1 = peg$FAILED;
1999 | if (peg$silentFails === 0) { peg$fail(peg$c73); }
2000 | }
2001 | if (s1 !== peg$FAILED) {
2002 | while (s1 !== peg$FAILED) {
2003 | s0.push(s1);
2004 | if (peg$c72.test(input.charAt(peg$currPos))) {
2005 | s1 = input.charAt(peg$currPos);
2006 | peg$currPos++;
2007 | } else {
2008 | s1 = peg$FAILED;
2009 | if (peg$silentFails === 0) { peg$fail(peg$c73); }
2010 | }
2011 | }
2012 | } else {
2013 | s0 = peg$FAILED;
2014 | }
2015 |
2016 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2017 |
2018 | return s0;
2019 | }
2020 |
2021 | function peg$parseregex() {
2022 | var s0, s1, s2, s3, s4;
2023 |
2024 | var key = peg$currPos * 33 + 22,
2025 | cached = peg$resultsCache[key];
2026 |
2027 | if (cached) {
2028 | peg$currPos = cached.nextPos;
2029 |
2030 | return cached.result;
2031 | }
2032 |
2033 | s0 = peg$currPos;
2034 | if (input.charCodeAt(peg$currPos) === 47) {
2035 | s1 = peg$c74;
2036 | peg$currPos++;
2037 | } else {
2038 | s1 = peg$FAILED;
2039 | if (peg$silentFails === 0) { peg$fail(peg$c75); }
2040 | }
2041 | if (s1 !== peg$FAILED) {
2042 | s2 = [];
2043 | if (peg$c76.test(input.charAt(peg$currPos))) {
2044 | s3 = input.charAt(peg$currPos);
2045 | peg$currPos++;
2046 | } else {
2047 | s3 = peg$FAILED;
2048 | if (peg$silentFails === 0) { peg$fail(peg$c77); }
2049 | }
2050 | if (s3 !== peg$FAILED) {
2051 | while (s3 !== peg$FAILED) {
2052 | s2.push(s3);
2053 | if (peg$c76.test(input.charAt(peg$currPos))) {
2054 | s3 = input.charAt(peg$currPos);
2055 | peg$currPos++;
2056 | } else {
2057 | s3 = peg$FAILED;
2058 | if (peg$silentFails === 0) { peg$fail(peg$c77); }
2059 | }
2060 | }
2061 | } else {
2062 | s2 = peg$FAILED;
2063 | }
2064 | if (s2 !== peg$FAILED) {
2065 | if (input.charCodeAt(peg$currPos) === 47) {
2066 | s3 = peg$c74;
2067 | peg$currPos++;
2068 | } else {
2069 | s3 = peg$FAILED;
2070 | if (peg$silentFails === 0) { peg$fail(peg$c75); }
2071 | }
2072 | if (s3 !== peg$FAILED) {
2073 | s4 = peg$parseflags();
2074 | if (s4 === peg$FAILED) {
2075 | s4 = null;
2076 | }
2077 | if (s4 !== peg$FAILED) {
2078 | peg$savedPos = s0;
2079 | s1 = peg$c78(s2, s4);
2080 | s0 = s1;
2081 | } else {
2082 | peg$currPos = s0;
2083 | s0 = peg$FAILED;
2084 | }
2085 | } else {
2086 | peg$currPos = s0;
2087 | s0 = peg$FAILED;
2088 | }
2089 | } else {
2090 | peg$currPos = s0;
2091 | s0 = peg$FAILED;
2092 | }
2093 | } else {
2094 | peg$currPos = s0;
2095 | s0 = peg$FAILED;
2096 | }
2097 |
2098 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2099 |
2100 | return s0;
2101 | }
2102 |
2103 | function peg$parsefield() {
2104 | var s0, s1, s2, s3, s4, s5, s6;
2105 |
2106 | var key = peg$currPos * 33 + 23,
2107 | cached = peg$resultsCache[key];
2108 |
2109 | if (cached) {
2110 | peg$currPos = cached.nextPos;
2111 |
2112 | return cached.result;
2113 | }
2114 |
2115 | s0 = peg$currPos;
2116 | if (input.charCodeAt(peg$currPos) === 46) {
2117 | s1 = peg$c43;
2118 | peg$currPos++;
2119 | } else {
2120 | s1 = peg$FAILED;
2121 | if (peg$silentFails === 0) { peg$fail(peg$c44); }
2122 | }
2123 | if (s1 !== peg$FAILED) {
2124 | s2 = peg$parseidentifierName();
2125 | if (s2 !== peg$FAILED) {
2126 | s3 = [];
2127 | s4 = peg$currPos;
2128 | if (input.charCodeAt(peg$currPos) === 46) {
2129 | s5 = peg$c43;
2130 | peg$currPos++;
2131 | } else {
2132 | s5 = peg$FAILED;
2133 | if (peg$silentFails === 0) { peg$fail(peg$c44); }
2134 | }
2135 | if (s5 !== peg$FAILED) {
2136 | s6 = peg$parseidentifierName();
2137 | if (s6 !== peg$FAILED) {
2138 | s5 = [s5, s6];
2139 | s4 = s5;
2140 | } else {
2141 | peg$currPos = s4;
2142 | s4 = peg$FAILED;
2143 | }
2144 | } else {
2145 | peg$currPos = s4;
2146 | s4 = peg$FAILED;
2147 | }
2148 | while (s4 !== peg$FAILED) {
2149 | s3.push(s4);
2150 | s4 = peg$currPos;
2151 | if (input.charCodeAt(peg$currPos) === 46) {
2152 | s5 = peg$c43;
2153 | peg$currPos++;
2154 | } else {
2155 | s5 = peg$FAILED;
2156 | if (peg$silentFails === 0) { peg$fail(peg$c44); }
2157 | }
2158 | if (s5 !== peg$FAILED) {
2159 | s6 = peg$parseidentifierName();
2160 | if (s6 !== peg$FAILED) {
2161 | s5 = [s5, s6];
2162 | s4 = s5;
2163 | } else {
2164 | peg$currPos = s4;
2165 | s4 = peg$FAILED;
2166 | }
2167 | } else {
2168 | peg$currPos = s4;
2169 | s4 = peg$FAILED;
2170 | }
2171 | }
2172 | if (s3 !== peg$FAILED) {
2173 | peg$savedPos = s0;
2174 | s1 = peg$c79(s2, s3);
2175 | s0 = s1;
2176 | } else {
2177 | peg$currPos = s0;
2178 | s0 = peg$FAILED;
2179 | }
2180 | } else {
2181 | peg$currPos = s0;
2182 | s0 = peg$FAILED;
2183 | }
2184 | } else {
2185 | peg$currPos = s0;
2186 | s0 = peg$FAILED;
2187 | }
2188 |
2189 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2190 |
2191 | return s0;
2192 | }
2193 |
2194 | function peg$parsenegation() {
2195 | var s0, s1, s2, s3, s4, s5;
2196 |
2197 | var key = peg$currPos * 33 + 24,
2198 | cached = peg$resultsCache[key];
2199 |
2200 | if (cached) {
2201 | peg$currPos = cached.nextPos;
2202 |
2203 | return cached.result;
2204 | }
2205 |
2206 | s0 = peg$currPos;
2207 | if (input.substr(peg$currPos, 5) === peg$c80) {
2208 | s1 = peg$c80;
2209 | peg$currPos += 5;
2210 | } else {
2211 | s1 = peg$FAILED;
2212 | if (peg$silentFails === 0) { peg$fail(peg$c81); }
2213 | }
2214 | if (s1 !== peg$FAILED) {
2215 | s2 = peg$parse_();
2216 | if (s2 !== peg$FAILED) {
2217 | s3 = peg$parseselectors();
2218 | if (s3 !== peg$FAILED) {
2219 | s4 = peg$parse_();
2220 | if (s4 !== peg$FAILED) {
2221 | if (input.charCodeAt(peg$currPos) === 41) {
2222 | s5 = peg$c69;
2223 | peg$currPos++;
2224 | } else {
2225 | s5 = peg$FAILED;
2226 | if (peg$silentFails === 0) { peg$fail(peg$c70); }
2227 | }
2228 | if (s5 !== peg$FAILED) {
2229 | peg$savedPos = s0;
2230 | s1 = peg$c82(s3);
2231 | s0 = s1;
2232 | } else {
2233 | peg$currPos = s0;
2234 | s0 = peg$FAILED;
2235 | }
2236 | } else {
2237 | peg$currPos = s0;
2238 | s0 = peg$FAILED;
2239 | }
2240 | } else {
2241 | peg$currPos = s0;
2242 | s0 = peg$FAILED;
2243 | }
2244 | } else {
2245 | peg$currPos = s0;
2246 | s0 = peg$FAILED;
2247 | }
2248 | } else {
2249 | peg$currPos = s0;
2250 | s0 = peg$FAILED;
2251 | }
2252 |
2253 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2254 |
2255 | return s0;
2256 | }
2257 |
2258 | function peg$parsematches() {
2259 | var s0, s1, s2, s3, s4, s5;
2260 |
2261 | var key = peg$currPos * 33 + 25,
2262 | cached = peg$resultsCache[key];
2263 |
2264 | if (cached) {
2265 | peg$currPos = cached.nextPos;
2266 |
2267 | return cached.result;
2268 | }
2269 |
2270 | s0 = peg$currPos;
2271 | if (input.substr(peg$currPos, 9) === peg$c83) {
2272 | s1 = peg$c83;
2273 | peg$currPos += 9;
2274 | } else {
2275 | s1 = peg$FAILED;
2276 | if (peg$silentFails === 0) { peg$fail(peg$c84); }
2277 | }
2278 | if (s1 !== peg$FAILED) {
2279 | s2 = peg$parse_();
2280 | if (s2 !== peg$FAILED) {
2281 | s3 = peg$parseselectors();
2282 | if (s3 !== peg$FAILED) {
2283 | s4 = peg$parse_();
2284 | if (s4 !== peg$FAILED) {
2285 | if (input.charCodeAt(peg$currPos) === 41) {
2286 | s5 = peg$c69;
2287 | peg$currPos++;
2288 | } else {
2289 | s5 = peg$FAILED;
2290 | if (peg$silentFails === 0) { peg$fail(peg$c70); }
2291 | }
2292 | if (s5 !== peg$FAILED) {
2293 | peg$savedPos = s0;
2294 | s1 = peg$c85(s3);
2295 | s0 = s1;
2296 | } else {
2297 | peg$currPos = s0;
2298 | s0 = peg$FAILED;
2299 | }
2300 | } else {
2301 | peg$currPos = s0;
2302 | s0 = peg$FAILED;
2303 | }
2304 | } else {
2305 | peg$currPos = s0;
2306 | s0 = peg$FAILED;
2307 | }
2308 | } else {
2309 | peg$currPos = s0;
2310 | s0 = peg$FAILED;
2311 | }
2312 | } else {
2313 | peg$currPos = s0;
2314 | s0 = peg$FAILED;
2315 | }
2316 |
2317 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2318 |
2319 | return s0;
2320 | }
2321 |
2322 | function peg$parseis() {
2323 | var s0, s1, s2, s3, s4, s5;
2324 |
2325 | var key = peg$currPos * 33 + 26,
2326 | cached = peg$resultsCache[key];
2327 |
2328 | if (cached) {
2329 | peg$currPos = cached.nextPos;
2330 |
2331 | return cached.result;
2332 | }
2333 |
2334 | s0 = peg$currPos;
2335 | if (input.substr(peg$currPos, 4) === peg$c86) {
2336 | s1 = peg$c86;
2337 | peg$currPos += 4;
2338 | } else {
2339 | s1 = peg$FAILED;
2340 | if (peg$silentFails === 0) { peg$fail(peg$c87); }
2341 | }
2342 | if (s1 !== peg$FAILED) {
2343 | s2 = peg$parse_();
2344 | if (s2 !== peg$FAILED) {
2345 | s3 = peg$parseselectors();
2346 | if (s3 !== peg$FAILED) {
2347 | s4 = peg$parse_();
2348 | if (s4 !== peg$FAILED) {
2349 | if (input.charCodeAt(peg$currPos) === 41) {
2350 | s5 = peg$c69;
2351 | peg$currPos++;
2352 | } else {
2353 | s5 = peg$FAILED;
2354 | if (peg$silentFails === 0) { peg$fail(peg$c70); }
2355 | }
2356 | if (s5 !== peg$FAILED) {
2357 | peg$savedPos = s0;
2358 | s1 = peg$c85(s3);
2359 | s0 = s1;
2360 | } else {
2361 | peg$currPos = s0;
2362 | s0 = peg$FAILED;
2363 | }
2364 | } else {
2365 | peg$currPos = s0;
2366 | s0 = peg$FAILED;
2367 | }
2368 | } else {
2369 | peg$currPos = s0;
2370 | s0 = peg$FAILED;
2371 | }
2372 | } else {
2373 | peg$currPos = s0;
2374 | s0 = peg$FAILED;
2375 | }
2376 | } else {
2377 | peg$currPos = s0;
2378 | s0 = peg$FAILED;
2379 | }
2380 |
2381 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2382 |
2383 | return s0;
2384 | }
2385 |
2386 | function peg$parsehas() {
2387 | var s0, s1, s2, s3, s4, s5;
2388 |
2389 | var key = peg$currPos * 33 + 27,
2390 | cached = peg$resultsCache[key];
2391 |
2392 | if (cached) {
2393 | peg$currPos = cached.nextPos;
2394 |
2395 | return cached.result;
2396 | }
2397 |
2398 | s0 = peg$currPos;
2399 | if (input.substr(peg$currPos, 5) === peg$c88) {
2400 | s1 = peg$c88;
2401 | peg$currPos += 5;
2402 | } else {
2403 | s1 = peg$FAILED;
2404 | if (peg$silentFails === 0) { peg$fail(peg$c89); }
2405 | }
2406 | if (s1 !== peg$FAILED) {
2407 | s2 = peg$parse_();
2408 | if (s2 !== peg$FAILED) {
2409 | s3 = peg$parsehasSelectors();
2410 | if (s3 !== peg$FAILED) {
2411 | s4 = peg$parse_();
2412 | if (s4 !== peg$FAILED) {
2413 | if (input.charCodeAt(peg$currPos) === 41) {
2414 | s5 = peg$c69;
2415 | peg$currPos++;
2416 | } else {
2417 | s5 = peg$FAILED;
2418 | if (peg$silentFails === 0) { peg$fail(peg$c70); }
2419 | }
2420 | if (s5 !== peg$FAILED) {
2421 | peg$savedPos = s0;
2422 | s1 = peg$c90(s3);
2423 | s0 = s1;
2424 | } else {
2425 | peg$currPos = s0;
2426 | s0 = peg$FAILED;
2427 | }
2428 | } else {
2429 | peg$currPos = s0;
2430 | s0 = peg$FAILED;
2431 | }
2432 | } else {
2433 | peg$currPos = s0;
2434 | s0 = peg$FAILED;
2435 | }
2436 | } else {
2437 | peg$currPos = s0;
2438 | s0 = peg$FAILED;
2439 | }
2440 | } else {
2441 | peg$currPos = s0;
2442 | s0 = peg$FAILED;
2443 | }
2444 |
2445 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2446 |
2447 | return s0;
2448 | }
2449 |
2450 | function peg$parsefirstChild() {
2451 | var s0, s1;
2452 |
2453 | var key = peg$currPos * 33 + 28,
2454 | cached = peg$resultsCache[key];
2455 |
2456 | if (cached) {
2457 | peg$currPos = cached.nextPos;
2458 |
2459 | return cached.result;
2460 | }
2461 |
2462 | s0 = peg$currPos;
2463 | if (input.substr(peg$currPos, 12) === peg$c91) {
2464 | s1 = peg$c91;
2465 | peg$currPos += 12;
2466 | } else {
2467 | s1 = peg$FAILED;
2468 | if (peg$silentFails === 0) { peg$fail(peg$c92); }
2469 | }
2470 | if (s1 !== peg$FAILED) {
2471 | peg$savedPos = s0;
2472 | s1 = peg$c93();
2473 | }
2474 | s0 = s1;
2475 |
2476 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2477 |
2478 | return s0;
2479 | }
2480 |
2481 | function peg$parselastChild() {
2482 | var s0, s1;
2483 |
2484 | var key = peg$currPos * 33 + 29,
2485 | cached = peg$resultsCache[key];
2486 |
2487 | if (cached) {
2488 | peg$currPos = cached.nextPos;
2489 |
2490 | return cached.result;
2491 | }
2492 |
2493 | s0 = peg$currPos;
2494 | if (input.substr(peg$currPos, 11) === peg$c94) {
2495 | s1 = peg$c94;
2496 | peg$currPos += 11;
2497 | } else {
2498 | s1 = peg$FAILED;
2499 | if (peg$silentFails === 0) { peg$fail(peg$c95); }
2500 | }
2501 | if (s1 !== peg$FAILED) {
2502 | peg$savedPos = s0;
2503 | s1 = peg$c96();
2504 | }
2505 | s0 = s1;
2506 |
2507 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2508 |
2509 | return s0;
2510 | }
2511 |
2512 | function peg$parsenthChild() {
2513 | var s0, s1, s2, s3, s4, s5;
2514 |
2515 | var key = peg$currPos * 33 + 30,
2516 | cached = peg$resultsCache[key];
2517 |
2518 | if (cached) {
2519 | peg$currPos = cached.nextPos;
2520 |
2521 | return cached.result;
2522 | }
2523 |
2524 | s0 = peg$currPos;
2525 | if (input.substr(peg$currPos, 11) === peg$c97) {
2526 | s1 = peg$c97;
2527 | peg$currPos += 11;
2528 | } else {
2529 | s1 = peg$FAILED;
2530 | if (peg$silentFails === 0) { peg$fail(peg$c98); }
2531 | }
2532 | if (s1 !== peg$FAILED) {
2533 | s2 = peg$parse_();
2534 | if (s2 !== peg$FAILED) {
2535 | s3 = [];
2536 | if (peg$c61.test(input.charAt(peg$currPos))) {
2537 | s4 = input.charAt(peg$currPos);
2538 | peg$currPos++;
2539 | } else {
2540 | s4 = peg$FAILED;
2541 | if (peg$silentFails === 0) { peg$fail(peg$c62); }
2542 | }
2543 | if (s4 !== peg$FAILED) {
2544 | while (s4 !== peg$FAILED) {
2545 | s3.push(s4);
2546 | if (peg$c61.test(input.charAt(peg$currPos))) {
2547 | s4 = input.charAt(peg$currPos);
2548 | peg$currPos++;
2549 | } else {
2550 | s4 = peg$FAILED;
2551 | if (peg$silentFails === 0) { peg$fail(peg$c62); }
2552 | }
2553 | }
2554 | } else {
2555 | s3 = peg$FAILED;
2556 | }
2557 | if (s3 !== peg$FAILED) {
2558 | s4 = peg$parse_();
2559 | if (s4 !== peg$FAILED) {
2560 | if (input.charCodeAt(peg$currPos) === 41) {
2561 | s5 = peg$c69;
2562 | peg$currPos++;
2563 | } else {
2564 | s5 = peg$FAILED;
2565 | if (peg$silentFails === 0) { peg$fail(peg$c70); }
2566 | }
2567 | if (s5 !== peg$FAILED) {
2568 | peg$savedPos = s0;
2569 | s1 = peg$c99(s3);
2570 | s0 = s1;
2571 | } else {
2572 | peg$currPos = s0;
2573 | s0 = peg$FAILED;
2574 | }
2575 | } else {
2576 | peg$currPos = s0;
2577 | s0 = peg$FAILED;
2578 | }
2579 | } else {
2580 | peg$currPos = s0;
2581 | s0 = peg$FAILED;
2582 | }
2583 | } else {
2584 | peg$currPos = s0;
2585 | s0 = peg$FAILED;
2586 | }
2587 | } else {
2588 | peg$currPos = s0;
2589 | s0 = peg$FAILED;
2590 | }
2591 |
2592 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2593 |
2594 | return s0;
2595 | }
2596 |
2597 | function peg$parsenthLastChild() {
2598 | var s0, s1, s2, s3, s4, s5;
2599 |
2600 | var key = peg$currPos * 33 + 31,
2601 | cached = peg$resultsCache[key];
2602 |
2603 | if (cached) {
2604 | peg$currPos = cached.nextPos;
2605 |
2606 | return cached.result;
2607 | }
2608 |
2609 | s0 = peg$currPos;
2610 | if (input.substr(peg$currPos, 16) === peg$c100) {
2611 | s1 = peg$c100;
2612 | peg$currPos += 16;
2613 | } else {
2614 | s1 = peg$FAILED;
2615 | if (peg$silentFails === 0) { peg$fail(peg$c101); }
2616 | }
2617 | if (s1 !== peg$FAILED) {
2618 | s2 = peg$parse_();
2619 | if (s2 !== peg$FAILED) {
2620 | s3 = [];
2621 | if (peg$c61.test(input.charAt(peg$currPos))) {
2622 | s4 = input.charAt(peg$currPos);
2623 | peg$currPos++;
2624 | } else {
2625 | s4 = peg$FAILED;
2626 | if (peg$silentFails === 0) { peg$fail(peg$c62); }
2627 | }
2628 | if (s4 !== peg$FAILED) {
2629 | while (s4 !== peg$FAILED) {
2630 | s3.push(s4);
2631 | if (peg$c61.test(input.charAt(peg$currPos))) {
2632 | s4 = input.charAt(peg$currPos);
2633 | peg$currPos++;
2634 | } else {
2635 | s4 = peg$FAILED;
2636 | if (peg$silentFails === 0) { peg$fail(peg$c62); }
2637 | }
2638 | }
2639 | } else {
2640 | s3 = peg$FAILED;
2641 | }
2642 | if (s3 !== peg$FAILED) {
2643 | s4 = peg$parse_();
2644 | if (s4 !== peg$FAILED) {
2645 | if (input.charCodeAt(peg$currPos) === 41) {
2646 | s5 = peg$c69;
2647 | peg$currPos++;
2648 | } else {
2649 | s5 = peg$FAILED;
2650 | if (peg$silentFails === 0) { peg$fail(peg$c70); }
2651 | }
2652 | if (s5 !== peg$FAILED) {
2653 | peg$savedPos = s0;
2654 | s1 = peg$c102(s3);
2655 | s0 = s1;
2656 | } else {
2657 | peg$currPos = s0;
2658 | s0 = peg$FAILED;
2659 | }
2660 | } else {
2661 | peg$currPos = s0;
2662 | s0 = peg$FAILED;
2663 | }
2664 | } else {
2665 | peg$currPos = s0;
2666 | s0 = peg$FAILED;
2667 | }
2668 | } else {
2669 | peg$currPos = s0;
2670 | s0 = peg$FAILED;
2671 | }
2672 | } else {
2673 | peg$currPos = s0;
2674 | s0 = peg$FAILED;
2675 | }
2676 |
2677 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2678 |
2679 | return s0;
2680 | }
2681 |
2682 | function peg$parseclass() {
2683 | var s0, s1, s2;
2684 |
2685 | var key = peg$currPos * 33 + 32,
2686 | cached = peg$resultsCache[key];
2687 |
2688 | if (cached) {
2689 | peg$currPos = cached.nextPos;
2690 |
2691 | return cached.result;
2692 | }
2693 |
2694 | s0 = peg$currPos;
2695 | if (input.charCodeAt(peg$currPos) === 58) {
2696 | s1 = peg$c103;
2697 | peg$currPos++;
2698 | } else {
2699 | s1 = peg$FAILED;
2700 | if (peg$silentFails === 0) { peg$fail(peg$c104); }
2701 | }
2702 | if (s1 !== peg$FAILED) {
2703 | s2 = peg$parseidentifierName();
2704 | if (s2 !== peg$FAILED) {
2705 | peg$savedPos = s0;
2706 | s1 = peg$c105(s2);
2707 | s0 = s1;
2708 | } else {
2709 | peg$currPos = s0;
2710 | s0 = peg$FAILED;
2711 | }
2712 | } else {
2713 | peg$currPos = s0;
2714 | s0 = peg$FAILED;
2715 | }
2716 |
2717 | peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2718 |
2719 | return s0;
2720 | }
2721 |
2722 |
2723 | function nth(n) { return { type: 'nth-child', index: { type: 'literal', value: n } }; }
2724 | function nthLast(n) { return { type: 'nth-last-child', index: { type: 'literal', value: n } }; }
2725 | function strUnescape(s) {
2726 | return s.replace(/\\(.)/g, function(match, ch) {
2727 | switch(ch) {
2728 | case 'b': return '\b';
2729 | case 'f': return '\f';
2730 | case 'n': return '\n';
2731 | case 'r': return '\r';
2732 | case 't': return '\t';
2733 | case 'v': return '\v';
2734 | default: return ch;
2735 | }
2736 | });
2737 | }
2738 |
2739 |
2740 | peg$result = peg$startRuleFunction();
2741 |
2742 | if (peg$result !== peg$FAILED && peg$currPos === input.length) {
2743 | return peg$result;
2744 | } else {
2745 | if (peg$result !== peg$FAILED && peg$currPos < input.length) {
2746 | peg$fail(peg$endExpectation());
2747 | }
2748 |
2749 | throw peg$buildStructuredError(
2750 | peg$maxFailExpected,
2751 | peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
2752 | peg$maxFailPos < input.length
2753 | ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
2754 | : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
2755 | );
2756 | }
2757 | }
2758 |
2759 | return {
2760 | SyntaxError: peg$SyntaxError,
2761 | parse: peg$parse
2762 | };
2763 | });
2764 |
--------------------------------------------------------------------------------