{"id":13837697,"url":"https://github.com/rse/astq","last_synced_at":"2025-04-05T03:03:33.697Z","repository":{"id":26338347,"uuid":"29787101","full_name":"rse/astq","owner":"rse","description":"Abstract Syntax Tree (AST) Query Engine","archived":false,"fork":false,"pushed_at":"2024-03-08T22:37:44.000Z","size":1406,"stargazers_count":205,"open_issues_count":10,"forks_count":15,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-28T13:08:03.686Z","etag":null,"topics":["abstract","ast","query","syntax","tree"],"latest_commit_sha":null,"homepage":"https://npmjs.com/astq","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rse.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-01-24T18:37:10.000Z","updated_at":"2025-03-22T16:52:57.000Z","dependencies_parsed_at":"2022-08-30T16:11:15.042Z","dependency_job_id":"cb9e9fe0-e29f-4989-83eb-83e49795044d","html_url":"https://github.com/rse/astq","commit_stats":{"total_commits":366,"total_committers":3,"mean_commits":122.0,"dds":0.005464480874316946,"last_synced_commit":"176decb66a61e7cec9c7c7779f69525f162458ef"},"previous_names":[],"tags_count":94,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rse%2Fastq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rse%2Fastq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rse%2Fastq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rse%2Fastq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rse","download_url":"https://codeload.github.com/rse/astq/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247280260,"owners_count":20912967,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abstract","ast","query","syntax","tree"],"created_at":"2024-08-04T15:01:20.942Z","updated_at":"2025-04-05T03:03:33.681Z","avatar_url":"https://github.com/rse.png","language":"JavaScript","readme":"\nASTq\n====\n\nAbstract Syntax Tree (AST) Query Engine\n\n[![github (author stars)](https://img.shields.io/github/stars/rse?logo=github\u0026label=author%20stars\u0026color=%233377aa)](https://github.com/rse)\n[![github (author followers)](https://img.shields.io/github/followers/rse?label=author%20followers\u0026logo=github\u0026color=%234477aa)](https://github.com/rse)\n\u003cbr/\u003e\n[![npm (project release)](https://img.shields.io/npm/v/astq?logo=npm\u0026label=npm%20release\u0026color=%23cc3333)](https://npmjs.com/astq)\n[![npm (project downloads)](https://img.shields.io/npm/dm/asty?logo=npm\u0026label=npm%20downloads\u0026color=%23cc3333)](https://npmjs.com/astq)\n\nInstallation\n------------\n\n```shell\n$ npm install astq\n```\n\nAbout\n-----\n\nASTq is an Abstract Syntax Tree (AST) query engine library for\nJavaScript, i.e., it allows you to query nodes of an arbitary AST-style\nhierarchical data structure with the help of a powerful XPath-inspired\nquery language. ASTq can operate on arbitrary AST-style data structures\nthrough the help of pluggable access adapters.\n\nQuery Language\n--------------\n\nASTq uses an XPath-inspired Domain Specific Language (DSL)\nfor querying the supplied AST-style hierarchical data structure.\n\n### By Example\n\nAt its simplest form, a query looks like a POSIX filesystem path:\n\n    Foo/Bar/Quux\n\nThis means: query and return all nodes of type `Quux`, which in turn\nare childs of nodes of type `Bar`, which in turn are childs of nodes of\ntype `Foo`, which in turn has to be the start node.\n\nA little bit more sophisticated query, showing more features,\nlike axis, filter and optional whitespaces for padding:\n\n    // Foo [ /Bar [ @bar == 'baz1' || @bar == 'baz2' ] \u0026\u0026 /Quux ]\n\nThis means: query and return all nodes anywhere under the start node\nwhich are of type `Foo` and which have both childs of type `Bar` -- and\nwith an attribute `bar` of values `baz1` or `baz2` -- and childs of type\n`Quux`.\n\n### By Grammar\n\nIn general, a query consists of one or more individual query paths,\nseparated by comma. A path consists of a mandatory initial query step\nand optionally zero or more subsequent query steps.\n\nThe difference between initial and subsequent query steps is that an\ninitial query step does not need an axis while all subsequent query\nsteps require it. A query step consists of an (optional) AST node search\naxis, a (mandatory) AST node type match, an (optional) result marker \"!\"\nand an (optional) AST node filter expression:\n\n    query            ::= path (, path)*\n    path             ::= step-initial step-subsequent*\n    step-initial     ::= axis? match result? filter?\n    step-subsequent  ::= axis  match result? filter?\n\nThe search axis can be either...\n\n- `/`    for direct child nodes, or\n- `//`   for any descendant nodes, or\n- `./`   for current node plus direct child nodes, or\n- `.//`  for current node plus any descendant nodes, or\n- `-/`   for direct left sibling node, or\n- `-//`  for any left sibling nodes, or\n- `+/`   for direct right sibling node, or\n- `+//`  for any right sibling nodes, or\n- `~/`   for direct left and right sibling nodes, or\n- `~//`  for all left and right sibling nodes, or\n- `../`  for direct parent node, or\n- `..//` for any parent nodes, or\n- `\u003c//`  for any preceding nodes, or\n- `\u003e//`  for any following nodes.\n\nAs an illustrating example: given an AST of the following particular nodes, ...\n\n          A\n          |\n      +-+-+-+-+\n     / /  |  \\ \\\n    B  C  D  E  F\n          |\n       +--+--+\n      /   |   \\\n     G    H    I\n          |\n        +-+-+\n       /     \\\n      J       K\n\n...the following queries and their result exist:\n\nStart Node | Query    | Result Node(s)\n-----------|----------|---------------------------------\n`D`        | `/    *` | `G, H, I`\n`D`        | `//   *` | `G, H, J, K, I`\n`D`        | `./   *` | `D, G, H, I`\n`D`        | `.//  *` | `D, G, H, J, K, I`\n`D`        | `-/   *` | `C`\n`D`        | `-//  *` | `C, B`\n`D`        | `+/   *` | `E`\n`D`        | `+//  *` | `E, F`\n`D`        | `~/   *` | `C, E`\n`D`        | `~//  *` | `B, C, E, F`\n`H`        | `../  *` | `D`\n`H`        | `..// *` | `D, A`\n`H`        | `\u003c//  *` | `G, D, C B A`\n`H`        | `\u003e//  *` | `J, K, I, E, F`\n\nA search axis usually walks along the references between nodes (at least\nin case of ASTy based AST). But in case the underlying AST and its\nadapter uses typed references, you can optionally constrain the search\naxis to take only references matching the type `id` into account.\n\n    axis               ::= axis-direction axis-type?\n    axis-direction     ::= axis-child\n                         | axis-sibling-left\n                         | axis-sibling-right\n                         | axis-sibling\n                         | axis-parent\n                         | axis-preceding\n                         | axis-following\n    axis-child         ::= (\"/\" | \"//\" | \"./\" | \".//\")\n    axis-sibling-left  ::= (\"-/\" | \"-//\")\n    axis-sibling-right ::= (\"+/\" | \"+//\")\n    axis-sibling       ::= (\"~/\" | \"~//\")\n    axis-parent        ::= (\"../\" | \"..//\")\n    axis-preceding     ::= \"\u003c//\"\n    axis-following     ::= \"\u003e//\"\n    axis-type          ::= \":\" (id | string)\n    result             ::= \"!\"\n    match              ::= id | string | \"*\"\n    filter             ::= \"[\" expr \"]\"\n\nThe real power comes through the optional filter expression: it can be\napplied to each query step and it recursively(!) can contain sub-queries\nwith the help of embedded query paths!\nAn illustrating combined example is:\n\n    // Foo / Bar [ / Baz [ @bar == 'baz' ] \u0026\u0026 / Quux ], // Foo2\n    +---------------------------------------------------------+  query\n    +------------------------------------------------+  +-----+  path\n                   +---------------------+    +-----+            path\n    +----+ +-----------------------------------------+  +-----+  step\n    ++     +       +                          +         ++       axis\n       +-+   +-+     +-+                        +--+       +--+  match\n                 +-----------------------------------+           filter\n                   +-------------------------------+             expr\n                         +---------------+                       filter\n                           +----------+                          expr\n\nThe result of a query is always all nodes which match against the last\nquery step of any path (in case of no result marker on any step in the\npath) or all nodes of matched steps with a result marker. The queries in\nfilter expressions just lead to a boolean decision for the filter, but\nnever cause any resulting nodes theirself.\n\nAn expression can be either a ternary/binary conditional expression,\nlogical expression, bitwise expression, relational expression,\narithmethical expression, functional call, attribute reference, query\nparameter, literal value, parenthesis expression or path of a sub-query.\n\n    expr             ::= conditional\n                       | logical\n                       | bitwise\n                       | relational\n                       | arithmentical\n                       | function-call\n                       | attribute-ref\n                       | query-parameter\n                       | literal\n                       | parenthesis\n                       | sub-query\n    conditional      ::= expr \"?\" expr \":\" expr\n                       | expr \"?:\" expr\n    logical          ::= expr (\"\u0026\u0026\" | \"||\") expr\n                       | \"!\" expr\n    bitwise          ::= expr (\"\u0026\" | \"|\" | \"\u003c\u003c\" | \"\u003e\u003e\") expr\n                       | \"~\" expr\n    relational       ::= expr (\"==\" | \"!=\" | \"\u003c=\" | \"\u003e=\" | \"\u003c\" | \"\u003e\" | \"=~\" | \"!~\") expr\n    arithmethical    ::= expr (\"+\" | \"-\" | \"*\" | \"/\" | \"%\" | \"**\") expr\n    function-call    ::= id \"(\" (expr (\",\" expr)*)? \")\"\n    attribute-ref    ::= \"@\" (id | string)\n    query-parameter  ::= \"{\" id \"}\"\n    id               ::= /[a-zA-Z_][a-zA-Z0-9_-]*/\n    literal          ::= string | regexp | number | value\n    string           ::= /\"(\\\\\"|.)*\"/ | /'(\\\\'|.)*'/\n    regexp           ::= /`(\\\\`|.)*`/\n    number           ::= /\\d+(\\.\\d+)?$/\n    value            ::= \"true\" | \"false\" | \"null\" | \"NaN\" | \"undefined\"\n    parenthesis      ::= \"(\" expr \")\"\n    sub-query        ::= path           // \u003c-- ESSENTIAL RECURSION !!\n\nNotice that the function call parameters can be full expressions theirself,\nincluding (through the recursion over `sub-query` above) full query paths.\nThe available pre-defined standard functions are:\n\n- `type(): String`:\u003cbr/\u003e\n  Return type of current node.\n  Example: `type() == \"foo\"`\n\n- `attrs(sep: String): String`:\u003cbr/\u003e\n  Return the `sep`-separated concatenation of all attribute names of\n  current node. The `sep` string is alway also prepended and appended\n  for easier comparison of the result string.\n  Example: `attr(\",\") == \",foo,bar,\"`\n\n- `depth(): Number`:\u003cbr/\u003e\n  Return depth in AST of current node (counting from 1 for the root node).\n  Example: `depth() \u003c= 3`\n\n- `pos(): Number`:\u003cbr/\u003e\n  Return position of current node among sibling (counting from 1 for the first sibling).\n  Example: `pos() == 2`\n\n- `nth(pos: Number): Boolean`:\u003cbr/\u003e\n  Check whether position of current node among sibling is `pos` (counting from 1 for\n  the first sibling). Negative values for `pos` count from the last sibling backward,\n  i.e., `-1` is the last sibling.\n  Example: `nth(3)`\n\n- `first(): Boolean`:\u003cbr/\u003e\n  Shorthand for `nth(1)`.\n\n- `last(): Boolean`:\u003cbr/\u003e\n  Shorthand for `nth(-1)`.\n\n- `count(array: Object[]): Number`:\u003cbr/\u003e\n  Return the number of elements in `array`.\n  The `array` usually is either an externally passed-in parameter or a sub-query.\n  Example: `count({nodes}) \u003c= count(// *)`\n\n- `below(node: Node): Boolean`:\u003cbr/\u003e\n  Checks whether current node is somewhere below `node`, i.e.,\n  whether current node is a child or descendant of `node`. Usually,\n  this makes sense only if `node` is an externally passed-in parameter.\n  Example: `below({node})`.\n\n- `follows(node: Node): Boolean`:\u003cbr/\u003e\n  Checks whether current node is following `node`, i.e.,\n  whether current node comes after `node` in a standard\n  depth-first tree visit (where parents are visited before childs).\n  Usually, this makes sense only if `node` is an externally passed-in parameter.\n  Example: `follows({node})`.\n\n- `in(nodes: Node[]): Number`:\u003cbr/\u003e\n  Checks whether current node is in `nodes`.\n  Usually, `nodes` is either an externally passed-in parameter or a sub-query.\n  Example: `in({nodes})`.\n\n- `substr(str: String, pos: Number, len: Number): String`:\u003cbr/\u003e\n  Returns the sub-string of `str`, starting at `pos` with length `len`.\n  Negative values for `pos` count from the end of the string,\n  i.e., `-1` is the last character.\n  Example: `substr(@foo, 0, 1) == \"A\"`\n\n- `index(str: String, sub: String, pos: Number): Number`:\u003cbr/\u003e\n  Returns the index position of sub-string `sub` in string `str`, starting at `pos`.\n  Example: `indexof(@foo, \"bar\", 0) \u003e= 0`\n\n- `trim(str: String): String`:\u003cbr/\u003e\n  Returns the string `str` with whitespaces removed from begin and end.\n  Example: `trim(@foo) == \"bar\"`\n\n- `lc(str: String): String`:\u003cbr/\u003e\n  Returns the lower-case variant of `str`.\n  Example: `lc(@foo) == \"bar\"`\n\n- `uc(str: String): String`:\u003cbr/\u003e\n  Returns the upper-case variant of `str`.\n  Example: `uc(@foo) == \"BAR\"`\n\nApplication Programming Interface (API)\n---------------------------------------\n\nThe ASTq API, here assumed to be exposed through the variable `ASTQ`,\nprovides the following methods (in a notation somewhat resembling\nTypeScript type definitions):\n\n### ASTQ API\n\n- `new ASTQ(): ASTQ`:\u003cbr/\u003e\n  Create a new ASTQ instance.\n\n- `ASTQ#adapter(adapter: (ASTQAdapter | ASTQAdapter[]), force: Boolean): ASTQ`:\u003cbr/\u003e\n  Register one or more custom tree access adapter(s) to support arbitrary AST-style\n  data structures. The `ASTQAdapter` has to conform to a particular\n  duck-typed interface. See below for more information.\n  By default ASTq has built-in adapters for ASTy, XML DOM, Parse5, Cheerio, UniST, JSON and Mozilla AST.\n  All those \"taste\" the node passed to `ASTQ#query` and hence are auto-selected.\n  Calling `adapter()` causes these to be replaced with a single custom adapter.\n  Its \"tasting\" can be disabled with option `force` set to `true`.\n  The `ASTQ#adapter` teturns the API itself.\n\n        /*  the built-in implementation for supporting ASTy  */\n        astq.adapter({\n            taste:            function (node)       { return (typeof node === \"object\" \u0026\u0026 node.ASTy) },\n            getParentNode:    function (node, type) { return node.parent()  },\n            getChildNodes:    function (node, type) { return node.childs()  },\n            getNodeType:      function (node)       { return node.type()    },\n            getNodeAttrNames: function (node)       { return node.attrs()   },\n            getNodeAttrValue: function (node, attr) { return node.get(attr) }\n        })\n\n- `ASTQ#version(): { major: Number, minor: Number, micro: Number, date: Number }`:\u003cbr/\u003e\n  Return the current ASTq library version details.\n\n- `ASTQ#func(name: String, func: (adapter: Adapter, node: Object, [...]) =\u003e Any): ASTQ`:\u003cbr/\u003e\n  Register function named `name` by providing the callback `func` which has\n  to return an arbitrary value and optionally can access the current `node` with\n  the help of the selected `adapter`. Returns the API itself.\n\n        /*  the built-in implementation for \"depth\"  */\n        astq.func(\"depth\", function (adapter, node) =\u003e {\n            var depth = 1\n            while ((node = adapter.getParentNode(node)) !== null)\n                depth++\n            return depth\n        })\n\n- `ASTQ#cache(num: Number): ASTQ`:\u003cbr/\u003e\n  Set the upper limit for the internal query cache to `num`, i.e.,\n  up to `num` ASTs of parsed queries will be cached. Set `num` to\n  `0` to disable the cache at all. Returns the API itself.\n\n- `ASTQ#compile(selector: String, trace?: Boolean): ASTQQuery {\n  Compile `selector` DSL into an internal query object for subsequent\n  processing by `ASTQ#execute`.\n  If `trace` is `true` the compiling is dumped to the console.\n  Returns the query object.\n\n- `ASTQ#execute(node: Object, query: ASTQQuery, params?: Object, trace?: Boolean): Object[]`:\u003cbr/\u003e\n  Execute the previously compiled `query` (see `compile` above) at `node`.\n  The optional `params` object can provide parameters for the `{name}` query constructs.\n  If `trace` is `true` the execution is dumped to the console.\n  Returns an array of zero or more matching AST nodes.\n\n- `ASTQ#query(node: Object, selector: String, params?: Object, trace?: Boolean): Object[]`: \u003cbr/\u003e\n  Just the convenient combination of `compile` and `execute`:\n  `execute(node, compile(selector, trace), params, trace)`.\n  Use this as the standard query method except you need more control.\n  The optional `params` object can provide parameters for the `{name}` query constructs.\n  If `trace` is `true` the compiling and execution is dumped to the console.\n  Returns an array of zero or more matching AST nodes.\n\n### ASTQAdapter API\n\nFor accessing arbitrary AST-style data structures, an adapter has to be\nprovided. By default ASTq has adapters for use with ASTy, XML DOM, Parse5, Cheerio, UniST, JSON and\nMozilla AST. The `ASTQAdapter` interface is:\n\n- `ASTQAdapter#taste(node: Object): Boolean`:\u003cbr/\u003e\n  Taste `node` to be sure this adapter is intended to handle it.\n\n- `ASTQAdapter#getParentNode(node: Object): Object`:\u003cbr/\u003e\n  Return parent node of `node`. In case the underyling\n  data structure does not support traversing to parent nodes,\n  throw an exception.\n\n- `ASTQAdapter#getChildNodes(node: Object): Object[]`:\u003cbr/\u003e\n  Return the list of all child nodes of `node`.\n\n- `ASTQAdapter#getNodeType(node: Object): String`:\u003cbr/\u003e\n  Return the type of `node`.\n\n- `ASTQAdapter#getNodeAttrNames(node: Object): String[]`:\u003cbr/\u003e\n  Return the list of all attribute names of `node`.\n\n- `ASTQAdapter#getNodeAttrValue(node: Object, attr: String): Any`:\u003cbr/\u003e\n  Return the value of attribute `attr` of `node`.\n\nExample\n-------\n\n```\n$ cat sample.js\nconst acorn = require(\"acorn\")\nconst ASTQ  = require(\"astq\")\n\nlet source = `\n    class Foo {\n        foo () {\n            const bar = \"quux\"\n            let baz = 42\n        }\n    }\n`\n\nlet ast = acorn.parse(source, { ecmaVersion: 6 })\n\nlet astq = new ASTQ()\nastq.adapter(\"mozast\")\nastq.query(ast, `\n    // VariableDeclarator [\n           /:id   Identifier [ @name  ]\n        \u0026\u0026 /:init Literal    [ @value ]\n    ]\n`).forEach(function (node) {\n    console.log(`${node.id.name}: ${node.init.value}`)\n})\n\n$ babel-node sample.js\nbar: quux\nbaz: 42\n```\n\nImplementation Notice\n---------------------\n\nAlthough ASTq is written in ECMAScript 2018, it is transpiled to older\nenvironments and this way runs in really all current (as of 2018)\nJavaScript environments, of course.\n\nAdditionally, there are two transpilation results: first, there is a\ncompressed `astq.browser.js` for Browser environments. Second, there is\nan uncompressed `astq.node.js` for Node.js environments.\n\nThe Browser variant `astq.browser.js` has all external dependencies `asty`,\n`pegjs-otf`, `pegjs-util`, and `cache-lru` directly embedded. The\nNode.js variant `astq.node.js` still requires the external dependencies\n`asty`, `pegjs-otf`, `pegjs-util`, and `cache-lru`.\n\nLicense\n-------\n\nCopyright \u0026copy; 2014-2024 Dr. Ralf S. Engelschall (http://engelschall.com/)\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frse%2Fastq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frse%2Fastq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frse%2Fastq/lists"}