elastic / kibana

Your window into the Elastic Stack
https://www.elastic.co/products/kibana
Other
19.48k stars 8.04k forks source link

[ES|QL] AST traversal API #182255

Open drewdaemon opened 2 months ago

drewdaemon commented 2 months ago

Finding things in an AST (and possibly changing them) requires a tree-traversal algorithm. Developers working with ES|QL queries shouldn't have to worry about implementing this themselves. Instead, we should provide a nice API.

Ideas

const nodes = findAll(ast, 'function', ['==', '!=']);
...

Or maybe

const matcher = (node, ctx) => ctx.depth == 1
  && isFunctionNode(node) 
  && ['==', '!='].includes(node.name);

const nodes = findAll(ast, matcher);

...

Or for manipulation, something like

const negateComparisons = (ast) => {
  walk(ast, (node) => {
    if (isFunctionNode(node) && node.name === '==') {
      node.name = '!=';
    }
  });
}
...

Could also provide typed shortcut methods like

const walker = new Walker(ast);
const functions = walker.findAllFunctions();
const constants = walker.findAllConstants();
const source = walker.findSource()

Each of those could return typed AST nodes, removing the need for consumers to run their own type checks.

Or we could implement a visitor like recast's

visit(ast, {
  onCommand: (node, ctx) => {
    messages.push(...validateCommand(node, ctx));
  },
  onFunction: ...
});

Some comparables

Some potential adoption points

elasticmachine commented 2 months ago

Pinging @elastic/kibana-esql (Team:ESQL)

drewdaemon commented 2 months ago

It would be great to use these walker APIs in our own validation engine as well.

mattkime commented 2 months ago

I agree that this looks very useful, but I think its something that might live inside a more opinionated service that takes the whole ESQL string and provides getters and mutators.

drewdaemon commented 2 months ago

@mattkime I think I get what you're saying, but I think a couple examples of how you would use the opinionated service to accomplish a given task would be helpful here. One recent task we had was adding a new where clause if it doesn't exist, or edit it if it does. How would you accomplish this with the service you're envisioning? What would the getters and mutators look like?

vadimkibana commented 2 weeks ago

There is an initial implementation of the Walker now available, it is missing:

stratoula commented 2 weeks ago

@vadimkibana also a Readme (documentation) when this is more mature would be very nice too.

drewdaemon commented 2 weeks ago

We may also want to consider giving the user more control over the traversal. Recast does this with this.traverse(), this.abort(), and return false;

vadimkibana commented 1 week ago

Acorn AST walker for inspiration: https://github.com/acornjs/acorn/tree/master/acorn-walk