web-dev-qa-db-fra.com

Analyseur JavaScript dans Python

Il y a un analyseur JavaScript au moins en C et Java (Mozilla), en JavaScript (Mozilla encore) et Ruby. Existe-t-il actuellement pour Python?

Je n'ai pas besoin d'un interpréteur JavaScript, en soi, juste un analyseur répondant aux normes ECMA-262.

Une recherche rapide sur Google n'a révélé aucune réponse immédiate, je demande donc à la communauté SO.

47
Claudiu

ANTLR , ANother Tool for Language Recognition, est un outil de langage qui fournit un cadre pour la construction de reconnaisseurs, d'interprètes, de compilateurs et de traducteurs à partir de descriptions grammaticales contenant des actions dans un variété de langues cibles.

Le site ANTLR fournit de nombreuses grammaires, dont ne pour JavaScript .

En l'occurrence, il existe un API Python disponible - vous pouvez donc appeler le lexer (identificateur) généré à partir de la grammaire directement depuis Python (bonne chance).

17
gimel

De nos jours, il existe au moins un meilleur outil, appelé slimit :

SlimIt est un minifieur JavaScript écrit en Python. Il compile JavaScript en code plus compact afin qu'il se télécharge et s'exécute plus rapidement.

SlimIt fournit également une bibliothèque qui comprend un analyseur JavaScript, un lexer, une jolie imprimante et un visiteur d'arbre.

Démo:

Imaginez que nous ayons le code javascript suivant:

$.ajax({
    type: "POST",
    url: 'http://www.example.com',
    data: {
        email: '[email protected]',
        phone: '9999999999',
        name: 'XYZ'
    }
});

Et maintenant, nous devons obtenir les valeurs email, phone et name de l'objet data.

L'idée ici serait d'instancier un analyseur slimit, de visiter tous les nœuds, de filtrer toutes les affectations et de les placer dans le dictionnaire:

from slimit import ast
from slimit.parser import Parser
from slimit.visitors import nodevisitor


data = """
$.ajax({
    type: "POST",
    url: 'http://www.example.com',
    data: {
        email: '[email protected]',
        phone: '9999999999',
        name: 'XYZ'
    }
});
"""

parser = Parser()
tree = parser.parse(data)
fields = {getattr(node.left, 'value', ''): getattr(node.right, 'value', '')
          for node in nodevisitor.visit(tree)
          if isinstance(node, ast.Assign)}

print fields

Il imprime:

{'name': "'XYZ'", 
 'url': "'http://www.example.com'", 
 'type': '"POST"', 
 'phone': "'9999999999'", 
 'data': '', 
 'email': "'[email protected]'"}
37
alecxe

Comme pib l'a mentionné, pynarcissus est un tokenizer Javascript écrit en Python. Il semble avoir quelques aspérités, mais jusqu'à présent, il a bien fonctionné pour ce que je veux accomplir.

Mise à jour: A pris une autre fissure à pynarcissus et ci-dessous est une direction de travail pour utiliser PyNarcissus dans un système de modèle de visiteur. Malheureusement, mon client actuel a acheté la prochaine itération de mes expériences et a décidé de ne pas la rendre publique. Une version plus propre du code ci-dessous est sur Gist ici

from pynarcissus import jsparser
from collections import defaultdict

class Visitor(object):

    CHILD_ATTRS = ['thenPart', 'elsePart', 'expression', 'body', 'initializer']

def __init__(self, filepath):
    self.filepath = filepath
    #List of functions by line # and set of names
    self.functions = defaultdict(set)
    with open(filepath) as myFile:
        self.source = myFile.read()

    self.root = jsparser.parse(self.source, self.filepath)
    self.visit(self.root)


def look4Childen(self, node):
    for attr in self.CHILD_ATTRS:
        child = getattr(node, attr, None)
        if child:
            self.visit(child)

def visit_NOOP(self, node):
    pass

def visit_FUNCTION(self, node):
    # Named functions
    if node.type == "FUNCTION" and getattr(node, "name", None):
        print str(node.lineno) + " | function " + node.name + " | " + self.source[node.start:node.end]


def visit_IDENTIFIER(self, node):
    # Anonymous functions declared with var name = function() {};
    try:
        if node.type == "IDENTIFIER" and hasattr(node, "initializer") and node.initializer.type == "FUNCTION":
            print str(node.lineno) + " | function " + node.name + " | " + self.source[node.start:node.initializer.end]
    except Exception as e:
        pass

def visit_PROPERTY_INIT(self, node):

    # Anonymous functions declared as a property of an object
    try:
        if node.type == "PROPERTY_INIT" and node[1].type == "FUNCTION":
            print str(node.lineno) + " | function " + node[0].value + " | " + self.source[node.start:node[1].end]
    except Exception as e:
        pass


def visit(self, root):

    call = lambda n: getattr(self, "visit_%s" % n.type, self.visit_NOOP)(n)
    call(root)
    self.look4Childen(root)
    for node in root:
        self.visit(node)

filepath = r"C:\Users\dward\Dropbox\juggernaut2\juggernaut\parser\test\data\jasmine.js"
outerspace = Visitor(filepath)
11
David

J'ai traduit esprima.js en Python:

https://github.com/PiotrDabkowski/pyjsparser

C'est une traduction manuelle, donc c'est très rapide, il faut environ 1 seconde pour analyser angular.js fichier (donc 100k caractères par seconde). Il prend en charge tout ECMAScript 5.1 et des parties de la version 6 - par exemple les fonctions Flèche, const, let.

Alternativement, vous pouvez utiliser traduction automatique de la nouvelle version d'esprima à python qui fonctionne très bien et prend en charge JavaScript 6 entier!

9
Piotr Dabkowski

Vous pouvez essayer python-spidermonkey Il s'agit d'un wrapper sur spidermonkey qui est le nom de code pour l'implémentation C de Mozilla en javascript.

3
swamy