Python中的JavaScript解析器

IT技术 javascript python parsing compiler-construction
2021-01-18 06:05:48

至少在 C 和 Java(Mozilla)、JavaScript(又是 Mozilla)和 Ruby 中有一个 JavaScript 解析器。目前有没有适用于 Python 的?

我不需要 JavaScript 解释器本身,只需要一个符合 ECMA-262 标准的解析器。

快速的谷歌搜索没有立即找到答案,所以我在问 SO 社区。

5个回答

如今,至少有一种更好的工具,称为slimit

SlimIt 是一个用 Python 编写的 JavaScript 压缩器。它将 JavaScript 编译成更紧凑的代码,以便下载和运行更快。

SlimIt 还提供了一个包含 JavaScript 解析器、词法分析器、漂亮打印机和树访问器的库。

演示:

假设我们有以下 javascript 代码:

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

现在我们需要对象中获取email,phonenamedata

这里的想法是实例化一个slimit解析器,访问所有节点,过滤所有分配并将它们放入字典中:

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: 'abc@g.com',
        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

它打印:

{'name': "'XYZ'", 
 'url': "'http://www.example.com'", 
 'type': '"POST"', 
 'phone': "'9999999999'", 
 'data': '', 
 'email': "'abc@g.com'"}
这个 2013 年的库似乎无法解析 Javascript 类。
2021-03-17 06:05:48
Slimit 很好,但只支持 ES5 :(
2021-03-17 06:05:48
这太棒了!您也可以将每个值作为字典访问,例如: print fields['url']
2021-03-31 06:05:48

ANTLR,ANother Tool for Language Recognition,是一种语言工具,它提供了一个框架,用于从包含各种目标语言的动作的语法描述构建识别器、解释器、编译器和翻译器。

ANTLR 站点提供了许多语法,包括一种用于 JavaScript 的语法

碰巧的是,有一个Python API可用 - 因此您可以直接从 Python 调用从语法生成的词法分析器(识别器)(祝你好运)。

ANTLR 4 似乎还活着。
2021-03-25 06:05:48

我已将esprima.js翻译成 Python:

https://github.com/PiotrDabkowski/pyjsparser

>>> from pyjsparser import parse
>>> parse('var $ = "Hello!"')
{
"type": "Program",
"body": [
    {
        "type": "VariableDeclaration",
        "declarations": [
            {
                "type": "VariableDeclarator",
                "id": {
                    "type": "Identifier",
                    "name": "$"
                },
                "init": {
                    "type": "Literal",
                    "value": "Hello!",
                    "raw": '"Hello!"'
                }
            }
        ],
        "kind": "var"
    }
  ]
}

这是一个手动翻译,所以它非常快,解析angular.js文件大约需要 1 秒(所以每秒 100k 个字符)。它支持整个 ECMAScript 5.1 和部分版本 6 - 例如箭头函数、constlet

如果您需要支持所有最新的 JS6 功能,您可以使用Js2Py即时翻译esprima

import js2py
esprima = js2py.require("esprima@4.0.1")
esprima.parse("a = () => {return 11};")
# {'body': [{'expression': {'left': {'name': 'a', 'type': 'Identifier'}, 'operator': '=', 'right': {'async': False, 'body': {'body': [{'argument': {'raw': '11', 'type': 'Literal', 'value': 11}, 'type': 'ReturnStatement'}], 'type': 'BlockStatement'}, 'expression': False, 'generator': False, 'id': None, 'params': [], 'type': 'ArrowFunctionExpression'}, 'type': 'AssignmentExpression'}, 'type': 'ExpressionStatement'}], 'sourceType': 'script', 'type': 'Program'}
好的文档,你也可以在线测试一些代码,esprima.org/demo/parse.html
2021-03-19 06:05:48
经测试,运行良好。您可以使用它并重建例如一些由爬虫捕获的 JSON 数据。
2021-03-31 06:05:48

正如 pib 所提到的,pynarcissus是一个用 Python 编写的 Javascript 标记器。它似乎有一些粗糙的边缘,但到目前为止,我想要完成的工作一直很好。

更新:在 pynarcissus 上进行了另一个破解,下面是在类似系统的访问者模式中使用 PyNarcissus 的工作方向。不幸的是,我当前的客户购买了我的实验的下一个迭代,并决定不将其公开。下面代码的更清晰版本是这里的要点

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)
这不能从解析后的 AST 中发出 JavaScript,是吗?我想修改 AST 并发出新的 JavaScript,但它似乎无法做到这一点。
2021-03-15 06:05:48
@gsingh2011 不,它在解析方面有点挣扎,所以另一种方式超出了它的能力。
2021-03-27 06:05:48

您可以尝试的python-SpiderMonkey的 是在包装的SpiderMonkey这是Mozilla的C语言实现的JavaScript的代号。

不幸的是,蜘蛛猴在上游死了。
2021-03-15 06:05:48