Skip to content
This repository has been archived by the owner on May 19, 2018. It is now read-only.

Add a parseExpression method #210

Closed
jeromew opened this issue Oct 31, 2016 · 7 comments
Closed

Add a parseExpression method #210

jeromew opened this issue Oct 31, 2016 · 7 comments

Comments

@jeromew
Copy link
Contributor

jeromew commented Oct 31, 2016

Hello,

I would like to use babylon as a fast expression parser to build an AST based code generator for the pug templating language. I was pointed to is-expression-babylon (https://github.com/pugjs/is-expression-babylon) that has a getExpression method.
But this module is now broken (I believe it broke after the migration to rollup).

Currently I use the code

var ast = babylon.parse('expr='+expr).program.body.pop().expression.right

it works but I suspect that this is not the most efficient way to simply parse an expression.

Would it be possible for someone with good knowledge of babylon internals to export an efficient parseExpression method ?

I could try an write a PR but would need directions. It is hard for me to know if what is-expression-babylon did is the most efficient use of babylon's internals.

Thanks for your help.

@SerkanSipahi
Copy link

SerkanSipahi commented Oct 31, 2016

Try this:

Example 1:

import * as babylon from "babylon";
import traverse from "babel-traverse";

const ast = babylon.parse('expr='+exprfoo);
traverse(ast, {
    // will called each time when "BinaryExpression" is found
    BinaryExpression(path){
        let res = path.get('right') // log: exprfoo
    },
});

see handbook, very useful: https://github.com/thejameskyle/babel-handbook/blob/master/translations/de/plugin-handbook.md

babel-types: https://github.com/babel/babel/blob/master/packages/babel-types/README.md

@loganfsmyth
Copy link
Member

Personally I'd wrap it in parens and go with that, e.g.

babylon.parse('(' + expr + ')').program.body[0].expression

I'm somewhat hesitant to add an API for this since it's easy enough to do with the existing one.

@jeromew
Copy link
Contributor Author

jeromew commented Oct 31, 2016

@SerkanSipahi thanks for the snippet. Nevertheless, babel-traverse adds serious slowness compared to the access to raw fields. I did a small benchmark and had around 130K ops/sec with my version (same for @loganfsmyth's version) versus 8K ops/sec using babel-traverse, just to parse "a".

I am trying to use a subset of what babylon does, trying to see if there can be an entry point inside babylon that would improve the performance of building the AST of something we know should be an expression (and throw if not)

@loganfsmyth your solution with "(..)" is maybe more robust than mine with "expr=.." I'll try that. I would have to revise my javascript 101 to make sure which pattern would always be parsed as an expression.

do you think I could get faster parsing with access to internals ? (skip the File and Program that I do not need, go directly to "start of an expression"). I need to parse a lot of small snippets which is why I want to avoid the cost of setup as much as i can.

@TimothyGu
Copy link
Contributor

And, another disadvantage with such a solution is that inputs like a)( will be parsed as a valid expression.

@jeromew
Copy link
Contributor Author

jeromew commented Oct 31, 2016

@TimothyGu indeed ! "=a" leads to an expression with "expr=.." so I guess none of these solutions are foolproof except if we really find a pattern that would force the expression parsing (your example made me realize that may not exist).

@jeromew
Copy link
Contributor Author

jeromew commented Oct 31, 2016

Seeing the code on https://github.com/pugjs/is-expression-babylon/blob/master/src/index.js and on https://github.com/babel/babylon/blob/master/src/parser/index.js, is-expression-babylon was close enough to the tokenizer to be a nice solution to the problem.

do you think that babylon could export the Parser or the Tokenizer so that we can sub-class it again ?

or would you agree with a PR that would allow for parsing specific sub-parts of js like is-expression-babylon was doing ? I believe the main idea was to force the parser into Expression mode and check that everything was parsed when leaving Expression mode :

  assertExpression() {
    this.nextToken();
    const expr = this.parseExpression();
    if (!this.match(tokTypes.eof)) {
      this.unexpected();
    }
    return expr;
  }
}

It would be great if we could have that again ! Feel free to comment on this and tell me what kind of PR has the best chance of beeing accepted (exporting the parser/tokenizer or a new assertExpression method)

Thanks !

@hzoo
Copy link
Member

hzoo commented May 26, 2017

Done in #213

@hzoo hzoo closed this as completed May 26, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants