至少在 C 和 Java(Mozilla)、JavaScript(又是 Mozilla)和 Ruby 中有一个 JavaScript 解析器。目前有没有适用于 Python 的?
我不需要 JavaScript 解释器本身,只需要一个符合 ECMA-262 标准的解析器。
快速的谷歌搜索没有立即找到答案,所以我在问 SO 社区。
至少在 C 和 Java(Mozilla)、JavaScript(又是 Mozilla)和 Ruby 中有一个 JavaScript 解析器。目前有没有适用于 Python 的?
我不需要 JavaScript 解释器本身,只需要一个符合 ECMA-262 标准的解析器。
快速的谷歌搜索没有立即找到答案,所以我在问 SO 社区。
如今,至少有一种更好的工具,称为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
,phone
和name
值data
。
这里的想法是实例化一个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'"}
ANTLR,ANother Tool for Language Recognition,是一种语言工具,它提供了一个框架,用于从包含各种目标语言的动作的语法描述构建识别器、解释器、编译器和翻译器。
ANTLR 站点提供了许多语法,包括一种用于 JavaScript 的语法。
碰巧的是,有一个Python API可用 - 因此您可以直接从 Python 调用从语法生成的词法分析器(识别器)(祝你好运)。
我已将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 - 例如箭头函数、const
、let
。
如果您需要支持所有最新的 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'}
正如 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)
您可以尝试的python-SpiderMonkey的 是在包装的SpiderMonkey这是Mozilla的C语言实现的JavaScript的代号。