GithubHelp home page GithubHelp logo

rse / astq Goto Github PK

View Code? Open in Web Editor NEW
200.0 6.0 15.0 1.37 MB

Abstract Syntax Tree (AST) Query Engine

Home Page: https://npmjs.com/astq

Makefile 1.55% JavaScript 89.52% PEG.js 8.93%
abstract syntax tree ast query

astq's Introduction

ASTq

Abstract Syntax Tree (AST) Query Engine

github (author stars) github (author followers)
npm (project release) npm (project downloads)

Installation

$ npm install astq

About

ASTq is an Abstract Syntax Tree (AST) query engine library for JavaScript, i.e., it allows you to query nodes of an arbitary AST-style hierarchical data structure with the help of a powerful XPath-inspired query language. ASTq can operate on arbitrary AST-style data structures through the help of pluggable access adapters.

Query Language

ASTq uses an XPath-inspired Domain Specific Language (DSL) for querying the supplied AST-style hierarchical data structure.

By Example

At its simplest form, a query looks like a POSIX filesystem path:

Foo/Bar/Quux

This means: query and return all nodes of type Quux, which in turn are childs of nodes of type Bar, which in turn are childs of nodes of type Foo, which in turn has to be the start node.

A little bit more sophisticated query, showing more features, like axis, filter and optional whitespaces for padding:

// Foo [ /Bar [ @bar == 'baz1' || @bar == 'baz2' ] && /Quux ]

This means: query and return all nodes anywhere under the start node which are of type Foo and which have both childs of type Bar -- and with an attribute bar of values baz1 or baz2 -- and childs of type Quux.

By Grammar

In general, a query consists of one or more individual query paths, separated by comma. A path consists of a mandatory initial query step and optionally zero or more subsequent query steps.

The difference between initial and subsequent query steps is that an initial query step does not need an axis while all subsequent query steps require it. A query step consists of an (optional) AST node search axis, a (mandatory) AST node type match, an (optional) result marker "!" and an (optional) AST node filter expression:

query            ::= path (, path)*
path             ::= step-initial step-subsequent*
step-initial     ::= axis? match result? filter?
step-subsequent  ::= axis  match result? filter?

The search axis can be either...

  • / for direct child nodes, or
  • // for any descendant nodes, or
  • ./ for current node plus direct child nodes, or
  • .// for current node plus any descendant nodes, or
  • -/ for direct left sibling node, or
  • -// for any left sibling nodes, or
  • +/ for direct right sibling node, or
  • +// for any right sibling nodes, or
  • ~/ for direct left and right sibling nodes, or
  • ~// for all left and right sibling nodes, or
  • ../ for direct parent node, or
  • ..// for any parent nodes, or
  • <// for any preceding nodes, or
  • >// for any following nodes.

As an illustrating example: given an AST of the following particular nodes, ...

      A
      |
  +-+-+-+-+
 / /  |  \ \
B  C  D  E  F
      |
   +--+--+
  /   |   \
 G    H    I
      |
    +-+-+
   /     \
  J       K

...the following queries and their result exist:

Start Node Query Result Node(s)
D / * G, H, I
D // * G, H, J, K, I
D ./ * D, G, H, I
D .// * D, G, H, J, K, I
D -/ * C
D -// * C, B
D +/ * E
D +// * E, F
D ~/ * C, E
D ~// * B, C, E, F
H ../ * D
H ..// * D, A
H <// * G, D, C B A
H >// * J, K, I, E, F

A search axis usually walks along the references between nodes (at least in case of ASTy based AST). But in case the underlying AST and its adapter uses typed references, you can optionally constrain the search axis to take only references matching the type id into account.

axis               ::= axis-direction axis-type?
axis-direction     ::= axis-child
                     | axis-sibling-left
                     | axis-sibling-right
                     | axis-sibling
                     | axis-parent
                     | axis-preceding
                     | axis-following
axis-child         ::= ("/" | "//" | "./" | ".//")
axis-sibling-left  ::= ("-/" | "-//")
axis-sibling-right ::= ("+/" | "+//")
axis-sibling       ::= ("~/" | "~//")
axis-parent        ::= ("../" | "..//")
axis-preceding     ::= "<//"
axis-following     ::= ">//"
axis-type          ::= ":" (id | string)
result             ::= "!"
match              ::= id | string | "*"
filter             ::= "[" expr "]"

The real power comes through the optional filter expression: it can be applied to each query step and it recursively(!) can contain sub-queries with the help of embedded query paths! An illustrating combined example is:

// Foo / Bar [ / Baz [ @bar == 'baz' ] && / Quux ], // Foo2
+---------------------------------------------------------+  query
+------------------------------------------------+  +-----+  path
               +---------------------+    +-----+            path
+----+ +-----------------------------------------+  +-----+  step
++     +       +                          +         ++       axis
   +-+   +-+     +-+                        +--+       +--+  match
             +-----------------------------------+           filter
               +-------------------------------+             expr
                     +---------------+                       filter
                       +----------+                          expr

The result of a query is always all nodes which match against the last query step of any path (in case of no result marker on any step in the path) or all nodes of matched steps with a result marker. The queries in filter expressions just lead to a boolean decision for the filter, but never cause any resulting nodes theirself.

An expression can be either a ternary/binary conditional expression, logical expression, bitwise expression, relational expression, arithmethical expression, functional call, attribute reference, query parameter, literal value, parenthesis expression or path of a sub-query.

expr             ::= conditional
                   | logical
                   | bitwise
                   | relational
                   | arithmentical
                   | function-call
                   | attribute-ref
                   | query-parameter
                   | literal
                   | parenthesis
                   | sub-query
conditional      ::= expr "?" expr ":" expr
                   | expr "?:" expr
logical          ::= expr ("&&" | "||") expr
                   | "!" expr
bitwise          ::= expr ("&" | "|" | "<<" | ">>") expr
                   | "~" expr
relational       ::= expr ("==" | "!=" | "<=" | ">=" | "<" | ">" | "=~" | "!~") expr
arithmethical    ::= expr ("+" | "-" | "*" | "/" | "%" | "**") expr
function-call    ::= id "(" (expr ("," expr)*)? ")"
attribute-ref    ::= "@" (id | string)
query-parameter  ::= "{" id "}"
id               ::= /[a-zA-Z_][a-zA-Z0-9_-]*/
literal          ::= string | regexp | number | value
string           ::= /"(\\"|.)*"/ | /'(\\'|.)*'/
regexp           ::= /`(\\`|.)*`/
number           ::= /\d+(\.\d+)?$/
value            ::= "true" | "false" | "null" | "NaN" | "undefined"
parenthesis      ::= "(" expr ")"
sub-query        ::= path           // <-- ESSENTIAL RECURSION !!

Notice that the function call parameters can be full expressions theirself, including (through the recursion over sub-query above) full query paths. The available pre-defined standard functions are:

  • type(): String:
    Return type of current node. Example: type() == "foo"

  • attrs(sep: String): String:
    Return the sep-separated concatenation of all attribute names of current node. The sep string is alway also prepended and appended for easier comparison of the result string. Example: attr(",") == ",foo,bar,"

  • depth(): Number:
    Return depth in AST of current node (counting from 1 for the root node). Example: depth() <= 3

  • pos(): Number:
    Return position of current node among sibling (counting from 1 for the first sibling). Example: pos() == 2

  • nth(pos: Number): Boolean:
    Check whether position of current node among sibling is pos (counting from 1 for the first sibling). Negative values for pos count from the last sibling backward, i.e., -1 is the last sibling. Example: nth(3)

  • first(): Boolean:
    Shorthand for nth(1).

  • last(): Boolean:
    Shorthand for nth(-1).

  • count(array: Object[]): Number:
    Return the number of elements in array. The array usually is either an externally passed-in parameter or a sub-query. Example: count({nodes}) <= count(// *)

  • below(node: Node): Boolean:
    Checks whether current node is somewhere below node, i.e., whether current node is a child or descendant of node. Usually, this makes sense only if node is an externally passed-in parameter. Example: below({node}).

  • follows(node: Node): Boolean:
    Checks whether current node is following node, i.e., whether current node comes after node in a standard depth-first tree visit (where parents are visited before childs). Usually, this makes sense only if node is an externally passed-in parameter. Example: follows({node}).

  • in(nodes: Node[]): Number:
    Checks whether current node is in nodes. Usually, nodes is either an externally passed-in parameter or a sub-query. Example: in({nodes}).

  • substr(str: String, pos: Number, len: Number): String:
    Returns the sub-string of str, starting at pos with length len. Negative values for pos count from the end of the string, i.e., -1 is the last character. Example: substr(@foo, 0, 1) == "A"

  • index(str: String, sub: String, pos: Number): Number:
    Returns the index position of sub-string sub in string str, starting at pos. Example: indexof(@foo, "bar", 0) >= 0

  • trim(str: String): String:
    Returns the string str with whitespaces removed from begin and end. Example: trim(@foo) == "bar"

  • lc(str: String): String:
    Returns the lower-case variant of str. Example: lc(@foo) == "bar"

  • uc(str: String): String:
    Returns the upper-case variant of str. Example: uc(@foo) == "BAR"

Application Programming Interface (API)

The ASTq API, here assumed to be exposed through the variable ASTQ, provides the following methods (in a notation somewhat resembling TypeScript type definitions):

ASTQ API

  • new ASTQ(): ASTQ:
    Create a new ASTQ instance.

  • ASTQ#adapter(adapter: (ASTQAdapter | ASTQAdapter[]), force: Boolean): ASTQ:
    Register one or more custom tree access adapter(s) to support arbitrary AST-style data structures. The ASTQAdapter has to conform to a particular duck-typed interface. See below for more information. By default ASTq has built-in adapters for ASTy, XML DOM, Parse5, Cheerio, UniST, JSON and Mozilla AST. All those "taste" the node passed to ASTQ#query and hence are auto-selected. Calling adapter() causes these to be replaced with a single custom adapter. Its "tasting" can be disabled with option force set to true. The ASTQ#adapter teturns the API itself.

      /*  the built-in implementation for supporting ASTy  */
      astq.adapter({
          taste:            function (node)       { return (typeof node === "object" && node.ASTy) },
          getParentNode:    function (node, type) { return node.parent()  },
          getChildNodes:    function (node, type) { return node.childs()  },
          getNodeType:      function (node)       { return node.type()    },
          getNodeAttrNames: function (node)       { return node.attrs()   },
          getNodeAttrValue: function (node, attr) { return node.get(attr) }
      })
    
  • ASTQ#version(): { major: Number, minor: Number, micro: Number, date: Number }:
    Return the current ASTq library version details.

  • ASTQ#func(name: String, func: (adapter: Adapter, node: Object, [...]) => Any): ASTQ:
    Register function named name by providing the callback func which has to return an arbitrary value and optionally can access the current node with the help of the selected adapter. Returns the API itself.

      /*  the built-in implementation for "depth"  */
      astq.func("depth", function (adapter, node) => {
          var depth = 1
          while ((node = adapter.getParentNode(node)) !== null)
              depth++
          return depth
      })
    
  • ASTQ#cache(num: Number): ASTQ:
    Set the upper limit for the internal query cache to num, i.e., up to num ASTs of parsed queries will be cached. Set num to 0 to disable the cache at all. Returns the API itself.

  • ASTQ#compile(selector: String, trace?: Boolean): ASTQQuery { Compile selectorDSL into an internal query object for subsequent processing byASTQ#execute. If traceistrue` the compiling is dumped to the console. Returns the query object.

  • ASTQ#execute(node: Object, query: ASTQQuery, params?: Object, trace?: Boolean): Object[]:
    Execute the previously compiled query (see compile above) at node. The optional params object can provide parameters for the {name} query constructs. If trace is true the execution is dumped to the console. Returns an array of zero or more matching AST nodes.

  • ASTQ#query(node: Object, selector: String, params?: Object, trace?: Boolean): Object[]:
    Just the convenient combination of compile and execute: execute(node, compile(selector, trace), params, trace). Use this as the standard query method except you need more control. The optional params object can provide parameters for the {name} query constructs. If trace is true the compiling and execution is dumped to the console. Returns an array of zero or more matching AST nodes.

ASTQAdapter API

For accessing arbitrary AST-style data structures, an adapter has to be provided. By default ASTq has adapters for use with ASTy, XML DOM, Parse5, Cheerio, UniST, JSON and Mozilla AST. The ASTQAdapter interface is:

  • ASTQAdapter#taste(node: Object): Boolean:
    Taste node to be sure this adapter is intended to handle it.

  • ASTQAdapter#getParentNode(node: Object): Object:
    Return parent node of node. In case the underyling data structure does not support traversing to parent nodes, throw an exception.

  • ASTQAdapter#getChildNodes(node: Object): Object[]:
    Return the list of all child nodes of node.

  • ASTQAdapter#getNodeType(node: Object): String:
    Return the type of node.

  • ASTQAdapter#getNodeAttrNames(node: Object): String[]:
    Return the list of all attribute names of node.

  • ASTQAdapter#getNodeAttrValue(node: Object, attr: String): Any:
    Return the value of attribute attr of node.

Example

$ cat sample.js
const acorn = require("acorn")
const ASTQ  = require("astq")

let source = `
    class Foo {
        foo () {
            const bar = "quux"
            let baz = 42
        }
    }
`

let ast = acorn.parse(source, { ecmaVersion: 6 })

let astq = new ASTQ()
astq.adapter("mozast")
astq.query(ast, `
    // VariableDeclarator [
           /:id   Identifier [ @name  ]
        && /:init Literal    [ @value ]
    ]
`).forEach(function (node) {
    console.log(`${node.id.name}: ${node.init.value}`)
})

$ babel-node sample.js
bar: quux
baz: 42

Implementation Notice

Although ASTq is written in ECMAScript 2018, it is transpiled to older environments and this way runs in really all current (as of 2018) JavaScript environments, of course.

Additionally, there are two transpilation results: first, there is a compressed astq.browser.js for Browser environments. Second, there is an uncompressed astq.node.js for Node.js environments.

The Browser variant astq.browser.js has all external dependencies asty, pegjs-otf, pegjs-util, and cache-lru directly embedded. The Node.js variant astq.node.js still requires the external dependencies asty, pegjs-otf, pegjs-util, and cache-lru.

License

Copyright © 2014-2024 Dr. Ralf S. Engelschall (http://engelschall.com/)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

astq's People

Contributors

lguzzon avatar moritzhuether avatar rse avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

astq's Issues

Stop execution on first match

Would be awesome to have a parameter in execute() so it stops on first match (and returns a single Node). IMO this feature is commonly supported for query APIs and useful on searches that takes time and only one (or the first one) node is needed. (DOM querySelectorAll() vs querySelector()). Think on somebody querying lots of files in a large project, if only one node is needed then this could save lots of time.

Do you think this would be a viable feature for this project API ?

A couple of nice to haves related to this:

  • adapters being able to stop the search
  • API for pagination or at least result size limit.

Thanks!, great library

Transitive dependency has high security vulnerability

After updating pegjs-otf with newer lodash version, update astq to latest pegjs-otf

High            Command Injection                                
  Package         lodash                                                  
  Patched in      >=4.17.21                                          
  Dependency of   asty-astq                                      
  Path            asty-astq > astq > pegjs-otf > lodash                                                                     
  More info       https://npmjs.com/advisories/1673

Add tests for ASTQ#func and ASTQ#adapter methods

Would be great to have a working test demonstrating ASTQ#adapter and ASTQ@func methods (particularly the later has no example in readme). I think at some point you wanted to add a test (

astq.func("add", function (a, b) { return a + b })
) but that line can be removed and tests will still pass).

I will try to add tests for both APIs (ASTQ#func and ASTQ#adapter methods), For the later I will need to define a custom AST (probably similar to snippet's #9 (comment)). Would you accept a PR with these ? Thanks

Cannot create a bundle with browserify

Hey :),

Thanks for the lib. I tried to bundle it with browserify and I receivered the following error:

Error: Cannot find module './astq-util.js' from '.../node_modules/astq/lib'

Is there a chance that you could find few minutes to have a look at it?

Best regards,
Emil

Beispiele im WIKI

Hallo Ralf,
danke für die lib. Ich hab' einen Adapter für den aktuellen CoffeeScript-AST gemacht. Der zukünftige CS-AST wird - aller Voraussicht nach - Babel-kompatibel sein.
Ich würde mir noch das eine oder andere Beispiel wünschen. Möglicherweise im WIKI ?
Insbesondere die Möglichkeiten des Ob und Wie man mit einem match oder einem result umgeht.
Also ich kann bspw. alle require-expressions finden und als Ergebnis erhalten. Dann kann ich die Elemente, die ich brauche selbst extrahieren. Netter wäre es, wenn ich die gesuchten Teil-Elemente direkt benennen könnte - so wie in deinem Beispiel mit der /:id und /:init-Notation.
Ist es möglich ein Ergebnis weiter zu filtern ?

// Assign [
    / Value / Call / Value / IdentifierLiteral [@value=='require']
  ]

Die interessanten Teile befinden sich nun in einem anderen Pfad im Assign-Knoten :
Assign / Variable / Value / IdentifierLiteral

Ein kleines Beispiel, wie man eigene (Extraktions)-Funktionen verwendet wäre auch hilfreich ?
Ich könnte da auch gerne was im WIKI formulieren - sobald ich verstanden habe wie es gemacht wird.

LG Andreas

astq playground with syntax hightlight, autocomplete and several examples

I think I never announce a couple of projects I've made using astq some time ago that I think the maintainers will be happy to see In particular this playground which uses monaco-editor so users can edit astq queries with syntax highlight, autocomplete, error handling inline help, and online evaluation against the target code which in my case is TypeScript /JavaScript/JSX/TSX. I thought the author would be happy seeing its creation being represented like this:

https://cancerberosgx.github.io/demos/cannabis/typescript-ast-query-editor/

(use the editor on the left and press ctrl-space for auto-completion of operators, modifiers, literals, etc. hover with the mouse to get help). Provoke some errors - I think more could be done with errors and suggestions).

the monaco configuration is mostly this
this is monaco definitions for those features: https://github.com/cancerberoSgx/cannabis/blob/master/typescript-ast-query-editor/src/editor/query/queryEditor.ts (you can try their playground and this config to play around...)

The playground has several examples of the real project that is a JavaScript/ TypeScript AST query library based on astq and although some uses custom helper functions of my library could be helpful for people understanding this syntax.

I must say that while I'm pretty familiar with AST representations of several languages I had a hard time learning astq syntax to write useful queries in a language like JavaScript
BTW what do you think of a playground like this but with a less complex target language like json/xml that shows real-life examples -I think that should be ideal for non expert users trying to learn the syntax. I would really want to master and document the syntax and BTW was it created by you ? was inspired on another work (form where get more examples) ? - I could easily reproduce that playground and contribute with something similar/simpler here - but first: I would like to know from @rse what's the status of this project but mostly if you are motivated or viable discuss/help me understand the limitations or advance queries that I didn't figure it out how to accomplish (in a single or finite number of queries).

In general I think that by adding 5 or 10 examples - real world targetting asts like JavaScript's could boost this project which IMO has great potential but is hard to grasp (the syntax not the API) for non expert users like me.The docs seems complete and formal, but I think this syntax is beautiful and complex enough to deserve a a getting started like documentation with examples - I understand the basics of several parser languages and their differences and still I found it hard to write simple queries since the syntax is rich and today whey you way queries you tink about html selectors, path, globs - which are pretty different.

BTW ( are you the author of this syntax ? Was it inspired in another works - if so or if there is something similar I would appreciate a reference. ) . I'm would like to contribute with some getting started and example document (but first wanted to ask about the project, limitations, and your motivation/viability, for describe and review my docs)

For example, this other project of mine https://cancerberosgx.github.io/demos/univac/playground/ is a playground to parse several common programming language code and shows their AST structure in different ways. It uses different parsing technologies form peg.js, tree-sitter, antlr4 so the structure of trees varies a lot between them. As a next step I would like to introduce the ability to query the asts using a language which can be relatively useful in all these trees / and language real-world queries (which include imperative, declarative, functional, parser definition, bash, strongly typed, data only, asm). Naturally I thought about this project and my doubt is if the syntax supports something like the following or must I solve it with helper functions:

In the context of C++/TypeScript/JavaScript/Java I would like to build one query (or two) that get me:

"the method declarations_(child-type)_ which
second (child index) parameter (child type) is of type (child-type) X (attribute)
name (child-type) contains Y (attribute)
members of (ancestor-type) of classes (type) that implements_(child-type)_ directly or indirectly the interface named_(attribute)_ U"

The difficult is recursion (directly or indirectly) . With multiple queries or a program the algorithm could be : find all interfaces extending interface I. then and recursively find all interfaces extending those and so and so until all are found. Then form those interfaces do nothing similar to query all the lasses implementing them directly or indirectly. My doubts are in general to this regard, is it possible to recurse like that using the syntax ? or I need to solve this with a helper function and probably a language extension or a second language on top of this?. my library already has helpers for this but slow compared with a solution based on astq

Going back to my typescript parser library It was very intuitive to add a custom adapter

  • support interesting features like
  • represent files and folders as nodes too
  • TS/JS projects (tsconfig.json / package.json and project dependencies
  • JSX/TSX syntax support
  • a second language to support glob like patterns (so I can filter nodes using expressions like "src//service//specialFileName*/InterfaceDeclaration/**/**MethodSignature"
  • previous implies a definition for unique node path - for performance I'm caching each node path but probably I'm already destroying your work performance nevertheless
  • query Identifier references and even names in the local scope of a symbol, or the declaration of a symbol or the apparent type of a name or return value, etc (which is inferred by tsc
  • so many values here needs to be calculated and some are expensive - so there's a thin cache layer for that but performance.

thanks for the library hoping you are still available/motivated, and with excuses for my long msg which is not an issue, my greetings from Uruguay, Seba

last() and nth(-1) functions not working

I have two adapters, one I've created for HTML and one from astq-ts. Both appear to work great except the last() or nth(-1), nth(-2), etc. functions. first() and nth(1), etc. functions do work.

TypeScript descriptions

Would you accept a PR with TypeScript types descriptions (.d.ts) files for the API and the adapter ? I already have it, if so, I just need to add the jsdoc descriptions, tidy up a bit and add it to package.json. But before I wanted to ask you, thanks, keep it up!

Which version of SpiderMonkey AST supports getParentNode?

I'm receiving the error message stating that my version of SpiderMonkey AST doesn't support parent node traversal from /lib/astq.node.js:362.
The query I'm using is in fact trying to traverse parent nodes of an AST built with Acorn. The query is //Identifier[@name=='startTimer'] ..// *

What version of SpiderMonkey do I need to install to get this feature? Or is there a different adapter I should try and use?

Allow string as id

is it possible to allow a string literal as id ?
the language would change possibly to:

id ::= /[a-zA-Z_][a-zA-Z0-9_-]*/ | string

This is required when handling ASTs that use ids like #text

Your readme's example is incorrect

Not sure if this is still activly maintained but your example does not work.

I modified it slightly to log the entire object and I can't get anything no matter what selector I use.

const acorn = require("acorn")
const ASTQ = require("astq")

let source = `
    class Foo {
        foo () {
            const bar = "quux"
            let baz = 42
        }
    }
`

let ast = acorn.parse(source, { ecmaVersion: 6 })

// console.log(JSON.stringify(ast, null, '  '))

let astq = new ASTQ()
const hey = astq.query(ast, astq.query(ast, `
    // VariableDeclarator [
           /:id   Identifier [ @name  ]
        && /:init Literal    [ @value ]
    ]
`)
// .forEach(function (node) {
//   console.log(`${node.id.name}: ${node.init.value}`)
// })

console.log('hey', hey)
// This also doesn't work
const hey = astq.query(ast, astq.query(ast, `Program`)

more detailed error messages

Hi Ralf,

is it possible that you could add a simple information to
astq/src/astq.js Line 132
about the node that caused the no suitable adapter error

It is extremely horrible to determine the root cause of the error without the node that causes it.
Thanks

nth(n) ignores current axis id

"nth": (A, T, num) => {
num = parseInt(num, 10)
let parent = A.getParentNode(T, "*")
if (parent !== null) {
let pchilds = A.getChildNodes(parent, "*")
if (num < 0)
num = pchilds.length - (num + 1)
for (let i = 0; i < pchilds.length; i++)
if (pchilds[i] === T)
return ((i + 1) === num)
return false
}

It seems like nth is implemented by getting the parent, then getting the children on * axis, and finding the element in question.
This produces arguably unexpected cases where nth is called from within a filter on an axis with an id, e.g. I would expect

/ ExpressionStatement / CallExpression /:arguments * [
  nth(1)
]

running on the input ast of

a(1)

to find the 1st argument of the CallExpression (1-based indexing, ouch).
Instead, this query returns nothing, because the 1st child of the CallExpression on axis * is actually the function that was called, located on .callee.
What now? Always just +1 the parameter for nth in this case? Not pretty, and doesn't work on ast nodes with multiple variable-length child arrays.


While testing this, I've also found that

/ ExpressionStatement / CallExpression / *

finds 'Identifier', 'Literal', 'ExpressionStatement':
What is happening here? Why is ExpressionStatement, which is the parent of the CallExpression, being matched by this Query?
The readme defines the / Axis as:

  • / for direct child nodes

Edit: Turns out this last part is likely caused by astq considering the parent property (which I populated on my mozast to get nth to work) as a valid child axis.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.