Posts tagged "abstract-syntax-tree"

Understanding Javascript at a low level

Did you ever ask yourself how Javascript works under the hood? It's important to understand how the language we're working with works on a lower level.

 Top level view of the Javascript engine

  • Receive source code
  • Parse the code and produce an Abstract Syntax Tree (AST)
  • Interpret as byte code and execute it
  • The profiler checks for optimisations at run-time
  • The compiler creates optimized code and replaces it with the byte code

Javascript Engine

What does the parser do?

Parser

A parser takes the source code and creates an AST. First, the parser splits the source code into tokens. There are different kinds of tokens, e.g. let and new are keywords, while + is an operator.

The tokens are then used to build the AST. If something unexpected is found, a SyntaxError is thrown.

A SyntaxError is thrown when the Javascript engine finds a piece of codes that don't belong to the language syntax.

Example

Let's try out the parser (using this website).

> var answer = 6 * 7;
[
    {
        "type": "Keyword",
        "value": "var"
    },
    {
        "type": "Identifier",
        "value": "answer"
    },
    {
        "type": "Punctuator",
        "value": "="
    },
    {
        "type": "Numeric",
        "value": "6"
    },
    {
        "type": "Punctuator",
        "value": "*"
    },
    {
        "type": "Numeric",
        "value": "7"
    },
    {
        "type": "Punctuator",
        "value": ";"
    }
]

 Abstract Syntax Tree (AST)

It's a graph (data structure) that represents a program.

It's used in:

  • Javascript Engine
  • Bundlers: Webpack, Rollup, Parcel
  • Transpilers: Babel
  • Linters: ESLint, Prettify
  • Type Checkers: Typescript, Flow
  • Syntax Highlighters

You can check the generated AST in the AST Explorer.

Below is an example on how to add an ESLint rule.

export default function(context) {
  return {
    VariableDeclaration(node) {
    	// const variable type
     	if (node.kind === "const") {
        	const declaration = node.declarations[0];
          	
          	// make sure that the value it's a number
          	if (typeof declaration.init.value === "number") {
            	 if (declaration.id.name !== declaration.id.name.toUpperCase()) {
                   	context.report({
                      	node: declaration.id,
                      	message: "The constant name should be in uppercase",
                      	fix: function(fixer) {
                         	return fixer.replaceText(declaration.id, declaration.id.name.toUpperCase()) 
                        }
                    })
                 }
            }
        }
    }
  };
};

 Example using AST to extend an ESLint rule without fix option

Example using AST to extend an ESLint rule without fix option

 Example using AST to extend an ESLint rule with fix option

Example using AST to extend an ESLint rule with fix option