Search code examples
javascriptabstract-syntax-treeesprima

Unable to parse function with Esprima/Acorn: unexpected token '('


I am able to parse arrow functions using Esprima and Acorn, but using normal functions gives an error:

const esprima = require('esprima');
const acorn = require('acorn');

console.log(esprima.parseScript(` () => { console.log('Test')}`)); //works
console.log(acorn.parse(` () => { console.log('Test') }`); //works

console.log(esprima.parseScript(`function () { console.log('Test')}`)); // Unexpected token
console.log(acorn.parse(`function () { console.log('Test') }`); //Unexpected token

Any suggestions?


Solution

  • tl;dr

    If the line begins with the token function, it is a FunctionDeclaration, not a FunctionExpression. And a function declaration requires an identifier (in a FunctionExpression it is optional).

    --------

    The way that function () { console.log('Test') } code is written, makes it a function declaration (FunctionDeclaration), not a function expression. And a function declaration expects an identifier (in a function expression it is optional).

    Try it in Chrome console, you'll get the same error.

    To understand, you'll have to look into the grammar os ES6 (more below).

    The () => {} (ArrowFunction) is always an expression (specifically, believe it or not, an AssignmentExpression).


    A JavaScript, in ES6, Script is roughly a sequence of statements (StatementList, which is a sequence of StatementListItems).

    A StatementListItem is a Statement or a Declaration.

    Between Statement or a Declaration, the only possible expression is ExpressionStatement.

    ExpressionStatement is nothing more than an Expression.

    And in Expression you find the FunctionExpression.

    Because they start with the same token, I believe the FunctionDeclaration takes precedence over the FunctionExpression (the former is "less deep" down the grammar). So the parser consumes the token function an expects the FunctionDeclaration to continue, throwing the error.


    Getting rid of the error

    You can add an identifier, fulfilling the FunctionDeclaration's requirements:

    console.log(esprima.parseScript(`function test() { console.log('Test')}`));
    console.log(acorn.parse(`function test() { console.log('Test') }`);
    

    But, again, that makes it a FunctionDeclaration. To make your code a function expression alone, as pointed by @JaredSmith in comments, wrap it into (/)s:

    console.log(esprima.parseScript(`(function () { console.log('Test')} )`));
    console.log(acorn.parse(`(function () { console.log('Test') } )`);