AST, CST & Ferramentas Incríveis!!

@millermedeiros / Agosto 2014

Primeira regra...

Não use RegExp para analizar JavaScript!

Segunda regra...

Não use RegExp para manipular JavaScript!

rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ TO͇̹̺ͅƝ̴ȳ̳ TH̘Ë͖́̉ ͠P̯͍̭O̚​N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝S̨̥̫͎̭ͯ̿̔̀ͅ

http://stackoverflow.com/a/1732454

Como parsers funcionam?

Mas o que vem a ser AST?

AST é uma representação do "código" em formato de "árvore".

ast

AST é normalmente um formato de transição

usado para descrever a estrutura original do programa antes que o mesmo seja convertido para outro formato.

function sum(a, b) {
  return a + b;
}
{
  "type": "Program",
  "body": [
    {
      "type": "FunctionDeclaration",
      "id": {
        "type": "Identifier",
        "name": "sum"
      },
      "params": [
        {
          "type": "Identifier",
          "name": "a"
        },
        {
          "type": "Identifier",
          "name": "b"
        }
      ],
      "defaults": [],
      "body": {
        "type": "BlockStatement",
        "body": [
          {
            "type": "ReturnStatement",
            "argument": {
              "type": "BinaryExpression",
              "operator": "+",
              "left": {
                "type": "Identifier",
                "name": "a"
              },
              "right": {
                "type": "Identifier",
                "name": "b"
              }
            }
          }
        ]
      },
      "rest": null,
      "generator": false,
      "expression": false
    }
  ]
}

E por que isso é útil?

É muito útil para ferramentas que analizam código

  • lint / validação
  • análise de complexidade
  • autocomplete baseado no contexto
  • detecção de globais
  • ...

e ferramentas que alteram o código

  • minificação
  • formatação
  • refactoring
  • compilers
  • transpilers
  • ...

Estrutura da AST

(Esprima / SpiderMonkey Parser API)

Tokens (símbolos)

Tokens são grupos de caracteres gerados apartir da análise léxica do programa.

a + b
[
{ "type": "Identifier",
  "value": "a" },
{ "type": "Punctuator",
  "value": "+" },
{ "type": "Identifier",
  "value": "b" }
]

Nodes (nós)

"Nodes" são representações de estruturas base do programa apartir da análise sintática

a + b
{
  "type": "ExpressionStatement",
  "expression": {
    "type": "BinaryExpression",
    "operator": "+",
    "left": {
      "type": "Identifier",
      "name": "a"
    },
    "right": {
      "type": "Identifier",
      "name": "b"
    }
  }
}

você não precisa decorar todos os tipos de nós!

http://esprima.org/demo/parse.html

Nós e tokens também podem conter loc & range

{
  "type": "VariableDeclaration",
  "range": [ 34, 53 ],
  "loc": {
    "start": {
      "line": 2,
      "column": 0
    },
    "end": {
      "line": 2,
      "column": 19
    }
  },
  // ...
}

CST?

CST é uma representação concreta do código

Infelizmente ainda não existe um padrão definido para JavaScript

Kyle Simpson (@getify), tentou mas não foi pra frente: https://github.com/getify/concrete-syntax-tree

Desafio alguém da plateia a criar um formato fácil de usar!

Por que CST é importante?

Geram a mesma AST:

a + b
(((a + b)))

o segundo exemplo parece Lisp mas é JavaScript válido :P

Motivo

Esformatter

Formatador de JavaScript com mais de 200 opções de configuração.

https://github.com/millermedeiros/esformatter

special thanks to Jörn Zaefferer (@bassistance) for all the motivation/support!

One day esformatter will be "complete", don't give up!

Rocambole


https://github.com/millermedeiros/rocambole
var source = 'var foo = "bar";';
var ast = rocambole.parse(source);

rocambole.moonwalk(ast, function(node) {
  if (node.type === 'VariableDeclaration') {
    doStuffWithVar(node);
  }
});

Cada nó possui uma referência para o primeiro e último token

node.startToken
node.endToken

E os tokens formam uma LinkedList

function toString(node) {
  var str = '';
  var token = node.startToken;
  var last = node.endToken.next;
  while (token !== last) {
    str += token.raw || token.value;
    token = token.next;
  }
  return str;
}

http://moutjs.com

nodefy

Converte módulos escritos no formato AMD para node.js

https://github.com/millermedeiros/nodefy


http://jshint.com

http://ternjs.net

https://github.com/pahen/madge

Outras ferramentas

Construa as ferramentas do futuro!

Obrigado!

@millermedeiros

http://slides.millermedeiros.com/braziljs/ast/