зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1633437 - Support for test metadata r=acreskey
This patch adds support for tests metadata. A test script parser is added as well as a new "doc" flavor that can be used to display the script info in the command line. This parser will be the basis for building automated docs and scripts verifications if we want to do this. Differential Revision: https://phabricator.services.mozilla.com/D72800
This commit is contained in:
Родитель
dfbeca257b
Коммит
33f1eee4d5
|
@ -23,6 +23,7 @@ mozilla.pth:third_party/python/distro
|
|||
mozilla.pth:third_party/python/dlmanager
|
||||
mozilla.pth:third_party/python/ecdsa/src
|
||||
mozilla.pth:third_party/python/enum34
|
||||
mozilla.pth:third_party/python/esprima
|
||||
mozilla.pth:third_party/python/fluent.migrate
|
||||
mozilla.pth:third_party/python/fluent.syntax
|
||||
mozilla.pth:third_party/python/funcsigs
|
||||
|
|
|
@ -19,10 +19,18 @@ from mozperftest.system import get_layers as system_layers
|
|||
from mozperftest.browser import get_layers as browser_layers
|
||||
from mozperftest.metrics import get_layers as metrics_layers
|
||||
|
||||
FLAVORS = ["script", "doc"]
|
||||
|
||||
|
||||
class Options:
|
||||
|
||||
general_args = {
|
||||
"--flavor": {
|
||||
"choices": FLAVORS,
|
||||
"metavar": "{{{}}}".format(", ".join(FLAVORS)),
|
||||
"default": None,
|
||||
"help": "Only run tests of this flavor.",
|
||||
},
|
||||
"tests": {
|
||||
"nargs": "*",
|
||||
"metavar": "TEST",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
import os
|
||||
import random
|
||||
from functools import partial
|
||||
from mach.decorators import CommandProvider, Command
|
||||
from mozbuild.base import MachCommandBase, MachCommandConditions as conditions
|
||||
|
@ -14,6 +16,25 @@ def get_perftest_parser():
|
|||
|
||||
@CommandProvider
|
||||
class Perftest(MachCommandBase):
|
||||
def _build_test_list(self, tests, randomized=False):
|
||||
res = []
|
||||
for test in tests:
|
||||
if os.path.isfile(test):
|
||||
tests.append(test)
|
||||
elif os.path.isdir(test):
|
||||
for root, dirs, files in os.walk(test):
|
||||
for file in files:
|
||||
if not file.startswith("perftest"):
|
||||
continue
|
||||
res.append(os.path.join(root, file))
|
||||
if not randomized:
|
||||
res.sort()
|
||||
else:
|
||||
# random shuffling is used to make sure
|
||||
# we don't always run tests in the same order
|
||||
random.shuffle(res)
|
||||
return res
|
||||
|
||||
@Command(
|
||||
"perftest",
|
||||
category="testing",
|
||||
|
@ -24,6 +45,23 @@ class Perftest(MachCommandBase):
|
|||
def run_perftest(
|
||||
self, flavor="script", test_objects=None, resolve_tests=True, **kwargs
|
||||
):
|
||||
|
||||
MachCommandBase._activate_virtualenv(self)
|
||||
kwargs["tests"] = self._build_test_list(
|
||||
kwargs["tests"], randomized=flavor != "doc"
|
||||
)
|
||||
|
||||
if flavor == "doc":
|
||||
from mozperftest.utils import install_package
|
||||
|
||||
install_package(self.virtualenv_manager, "esprima")
|
||||
|
||||
from mozperftest.scriptinfo import ScriptInfo
|
||||
|
||||
for test in kwargs["tests"]:
|
||||
print(ScriptInfo(test))
|
||||
return
|
||||
|
||||
from mozperftest import MachEnvironment, Metadata
|
||||
|
||||
kwargs["test_objects"] = test_objects
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
from collections import defaultdict
|
||||
import re
|
||||
import os
|
||||
import textwrap
|
||||
|
||||
# This import will fail if esprima is not installed.
|
||||
# The package is vendored in python/third_party and also available at PyPI
|
||||
# The mach perftest command will make sure it's installed when --flavor doc
|
||||
# is being used.
|
||||
import esprima
|
||||
|
||||
|
||||
_INFO = """\
|
||||
%(filename)s
|
||||
%(filename_underline)s
|
||||
|
||||
%(description)s
|
||||
|
||||
Owner: %(owner)s
|
||||
Usage:
|
||||
%(usage)s
|
||||
|
||||
Description:
|
||||
%(long_description)s
|
||||
"""
|
||||
|
||||
|
||||
class MetadataDict(defaultdict):
|
||||
def __missing__(self, key):
|
||||
return "N/A"
|
||||
|
||||
|
||||
class ScriptInfo(MetadataDict):
|
||||
def __init__(self, script):
|
||||
super(ScriptInfo, self).__init__()
|
||||
filename = os.path.basename(script)
|
||||
self["filename"] = script, filename
|
||||
self["filename_underline"] = None, "-" * len(filename)
|
||||
self.script = script
|
||||
with open(script) as f:
|
||||
self.parsed = esprima.parseScript(f.read())
|
||||
|
||||
# looking for the exports statement
|
||||
for stmt in self.parsed.body:
|
||||
if (
|
||||
stmt.type != "ExpressionStatement"
|
||||
or stmt.expression.left is None
|
||||
or stmt.expression.left.property.name != "exports"
|
||||
or stmt.expression.right is None
|
||||
or stmt.expression.right.properties is None
|
||||
):
|
||||
continue
|
||||
|
||||
# now scanning the properties
|
||||
for prop in stmt.expression.right.properties:
|
||||
if prop.value.type == "Identifier":
|
||||
value = prop.value.name
|
||||
elif prop.value.type == "Literal":
|
||||
value = prop.value.value
|
||||
elif prop.value.type == "TemplateLiteral":
|
||||
# ugly
|
||||
value = prop.value.quasis[0].value.cooked.replace("\n", " ")
|
||||
value = re.sub("\s+", " ", value).strip()
|
||||
elif prop.value.type == "ArrayExpression":
|
||||
value = [e.value for e in prop.value.elements]
|
||||
else:
|
||||
raise ValueError(prop.value.type)
|
||||
# line wrapping
|
||||
if isinstance(value, str):
|
||||
repr = "\n".join(textwrap.wrap(value, break_on_hyphens=False))
|
||||
elif isinstance(value, list):
|
||||
repr = ", ".join(value)
|
||||
|
||||
self[prop.key.name] = value, repr
|
||||
|
||||
def __str__(self):
|
||||
reprs = dict((k, v[1]) for k, v in self.items())
|
||||
d = MetadataDict()
|
||||
d.update(reprs)
|
||||
return _INFO % d
|
|
@ -1,19 +0,0 @@
|
|||
async function setUp(context) {
|
||||
context.log.info("setUp example!");
|
||||
}
|
||||
|
||||
async function test(context, commands) {
|
||||
context.log.info("Test with setUp/tearDown example!");
|
||||
await commands.measure.start("https://www.sitespeed.io/");
|
||||
await commands.measure.start("https://www.mozilla.org/en-US/");
|
||||
}
|
||||
|
||||
async function tearDown(context) {
|
||||
context.log.info("tearDown example!");
|
||||
}
|
||||
|
||||
module.exports = { // eslint-disable-line
|
||||
setUp,
|
||||
tearDown,
|
||||
test,
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/* eslint-env node */
|
||||
"use strict";
|
||||
|
||||
var someVar;
|
||||
|
||||
async function setUp(context) {
|
||||
context.log.info("setUp example!");
|
||||
}
|
||||
|
||||
async function test(context, commands) {
|
||||
context.log.info("Test with setUp/tearDown example!");
|
||||
await commands.measure.start("https://www.sitespeed.io/");
|
||||
await commands.measure.start("https://www.mozilla.org/en-US/");
|
||||
}
|
||||
|
||||
async function tearDown(context) {
|
||||
context.log.info("tearDown example!");
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
setUp,
|
||||
tearDown,
|
||||
test,
|
||||
owner: "Performance Team",
|
||||
description: "Measures cold process applink time",
|
||||
long_description: `
|
||||
This test launches the appropriate android app, simulating an app link
|
||||
workflow. The application is launched with the intent action
|
||||
android.intent.action.VIEW loading a trivially simple website. The reported
|
||||
metric is the time from process start to navigationStart, reported as processLaunchToNavStart
|
||||
`,
|
||||
usage: `
|
||||
./mach perftest testing/performance/perftest_applink.js \
|
||||
--android-install-apk ~/fenix.v2.fennec-nightly.2020.04.22-arm32.apk \
|
||||
--hooks testing/performance/hooks_applink.py \
|
||||
--android-app-name org.mozilla.fennec_aurora \
|
||||
--perfherder-metrics processLaunchToNavStart
|
||||
`,
|
||||
supported_browser: ["Fenix nightly", "Geckoview_example", "Fennec"],
|
||||
platform: ["Android"],
|
||||
};
|
|
@ -8,6 +8,11 @@ from mozperftest.metadata import Metadata
|
|||
from mozperftest.environment import MachEnvironment
|
||||
|
||||
|
||||
HERE = os.path.dirname(__file__)
|
||||
EXAMPLE_TESTS_DIR = os.path.join(HERE, "samples")
|
||||
EXAMPLE_TEST = os.path.join(EXAMPLE_TESTS_DIR, "perftest_example.js")
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def temp_file(name="temp", content=None):
|
||||
tempdir = tempfile.mkdtemp()
|
||||
|
|
|
@ -4,7 +4,7 @@ import mozunit
|
|||
import mock
|
||||
import shutil
|
||||
|
||||
from mozperftest.tests.support import get_running_env
|
||||
from mozperftest.tests.support import get_running_env, EXAMPLE_TEST
|
||||
from mozperftest.environment import BROWSER
|
||||
from mozperftest.browser.browsertime import add_options
|
||||
from mozperftest.utils import silence
|
||||
|
@ -28,7 +28,7 @@ def fetch(self, url):
|
|||
def test_browser():
|
||||
mach_cmd, metadata, env = get_running_env()
|
||||
browser = env.layers[BROWSER]
|
||||
env.set_arg("tests", [os.path.join(HERE, "example.js")])
|
||||
env.set_arg("tests", [EXAMPLE_TEST])
|
||||
|
||||
try:
|
||||
with browser as b, silence():
|
||||
|
@ -38,7 +38,7 @@ def test_browser():
|
|||
|
||||
assert mach_cmd.run_process.call_count == 1
|
||||
# XXX more checks
|
||||
assert mach_cmd.run_process.call_args[0][-1][-1] == os.path.join(HERE, "example.js")
|
||||
assert mach_cmd.run_process.call_args[0][-1][-1] == EXAMPLE_TEST
|
||||
|
||||
|
||||
def test_add_options():
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import os
|
||||
import mozunit
|
||||
|
||||
from mozperftest.tests.support import get_running_env, temp_dir
|
||||
from mozperftest.tests.support import EXAMPLE_TEST, get_running_env, temp_dir
|
||||
from mozperftest.environment import METRICS
|
||||
from mozperftest.utils import silence
|
||||
|
||||
|
@ -26,7 +26,7 @@ def test_console_output():
|
|||
|
||||
mach_cmd.run_process = _run_process
|
||||
metrics = env.layers[METRICS]
|
||||
env.set_arg("tests", [os.path.join(HERE, "example.js")])
|
||||
env.set_arg("tests", [EXAMPLE_TEST])
|
||||
metadata.set_result(os.path.join(HERE, "browsertime-results"))
|
||||
|
||||
with metrics as console, silence():
|
||||
|
|
|
@ -7,6 +7,7 @@ import os
|
|||
import mock
|
||||
import tempfile
|
||||
import shutil
|
||||
from contextlib import contextmanager
|
||||
|
||||
from mach.registrar import Registrar
|
||||
|
||||
|
@ -16,6 +17,7 @@ Registrar.commands_by_category = {"testing": set()}
|
|||
|
||||
from mozperftest.environment import MachEnvironment
|
||||
from mozperftest.mach_commands import Perftest
|
||||
from mozperftest.tests.support import EXAMPLE_TESTS_DIR
|
||||
|
||||
|
||||
class _TestMachEnvironment(MachEnvironment):
|
||||
|
@ -29,8 +31,8 @@ class _TestMachEnvironment(MachEnvironment):
|
|||
pass
|
||||
|
||||
|
||||
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
|
||||
def test_command():
|
||||
@contextmanager
|
||||
def _get_perftest():
|
||||
from mozbuild.base import MozbuildObject
|
||||
|
||||
config = MozbuildObject.from_environment()
|
||||
|
@ -43,11 +45,25 @@ def test_command():
|
|||
state_dir = tempfile.mkdtemp()
|
||||
|
||||
try:
|
||||
test = Perftest(context())
|
||||
test.run_perftest()
|
||||
yield Perftest(context())
|
||||
finally:
|
||||
shutil.rmtree(context.state_dir)
|
||||
|
||||
|
||||
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
|
||||
@mock.patch("mozperftest.mach_commands.MachCommandBase._activate_virtualenv")
|
||||
def test_command(mocked_func):
|
||||
with _get_perftest() as test:
|
||||
test.run_perftest(tests=[EXAMPLE_TESTS_DIR])
|
||||
# XXX add assertions
|
||||
|
||||
|
||||
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
|
||||
@mock.patch("mozperftest.mach_commands.MachCommandBase._activate_virtualenv")
|
||||
def test_doc_flavor(mocked_func):
|
||||
with _get_perftest() as test:
|
||||
test.run_perftest(tests=[EXAMPLE_TESTS_DIR], flavor="doc")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mozunit.main()
|
||||
|
|
|
@ -3,7 +3,7 @@ import os
|
|||
import mozunit
|
||||
import json
|
||||
|
||||
from mozperftest.tests.support import get_running_env, temp_file
|
||||
from mozperftest.tests.support import get_running_env, temp_file, EXAMPLE_TEST
|
||||
from mozperftest.environment import METRICS
|
||||
from mozperftest.utils import silence
|
||||
|
||||
|
@ -22,7 +22,7 @@ def test_metrics():
|
|||
|
||||
mach_cmd.run_process = _run_process
|
||||
metrics = env.layers[METRICS]
|
||||
env.set_arg("tests", [os.path.join(HERE, "example.js")])
|
||||
env.set_arg("tests", [EXAMPLE_TEST])
|
||||
metadata.set_result(os.path.join(HERE, "browsertime-results"))
|
||||
|
||||
with temp_file() as output:
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
import mozunit
|
||||
|
||||
from mozperftest.scriptinfo import ScriptInfo
|
||||
from mozperftest.tests.support import EXAMPLE_TEST
|
||||
|
||||
|
||||
def test_scriptinfo():
|
||||
info = ScriptInfo(EXAMPLE_TEST)
|
||||
assert info["author"] == "N/A"
|
||||
|
||||
display = str(info)
|
||||
assert "appropriate android app" in display
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mozunit.main()
|
|
@ -4,6 +4,8 @@
|
|||
import logging
|
||||
import contextlib
|
||||
import sys
|
||||
import os
|
||||
|
||||
from six import StringIO
|
||||
|
||||
|
||||
|
@ -54,3 +56,20 @@ class MachLogger:
|
|||
|
||||
def error(self, msg, name="mozperftest", **kwargs):
|
||||
self._logger(logging.ERROR, name, kwargs, msg)
|
||||
|
||||
|
||||
def install_package(virtualenv_manager, package):
|
||||
from pip._internal.req.constructors import install_req_from_line
|
||||
|
||||
req = install_req_from_line(package)
|
||||
req.check_if_exists(use_user_site=False)
|
||||
# already installed, check if it's in our venv
|
||||
if req.satisfied_by is not None:
|
||||
venv_site_lib = os.path.abspath(
|
||||
os.path.join(virtualenv_manager.bin_path, "..", "lib")
|
||||
)
|
||||
site_packages = os.path.abspath(req.satisfied_by.location)
|
||||
if site_packages.startswith(venv_site_lib):
|
||||
# already installed in this venv, we can skip
|
||||
return
|
||||
virtualenv_manager._run_pip(["install", package])
|
||||
|
|
|
@ -5,23 +5,28 @@ from mozperftest.browser.browsertime import add_options
|
|||
|
||||
url = "'https://www.example.com'"
|
||||
|
||||
common_options = [("processStartTime", "true"),
|
||||
common_options = [
|
||||
("processStartTime", "true"),
|
||||
("firefox.disableBrowsertimeExtension", "true"),
|
||||
("firefox.android.intentArgument", "'-a'"),
|
||||
("firefox.android.intentArgument", "'android.intent.action.VIEW'"),
|
||||
("firefox.android.intentArgument", "'-d'"),
|
||||
("firefox.android.intentArgument", url)]
|
||||
("firefox.android.intentArgument", url),
|
||||
]
|
||||
|
||||
app_options = {
|
||||
"org.mozilla.geckoview_example": [
|
||||
("firefox.android.activity", "'org.mozilla.geckoview_example.GeckoViewActivity'")
|
||||
(
|
||||
"firefox.android.activity",
|
||||
"'org.mozilla.geckoview_example.GeckoViewActivity'",
|
||||
)
|
||||
],
|
||||
"org.mozilla.fennec_aurora": [
|
||||
("firefox.android.activity", "'org.mozilla.fenix.IntentReceiverActivity'")
|
||||
],
|
||||
"org.mozilla.firefox": [
|
||||
("firefox.android.activity", "'org.mozilla.gecko.BrowserApp'")
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
/* eslint-env node */
|
||||
"use strict";
|
||||
|
||||
module.exports = async function(context, commands) {
|
||||
"use strict";
|
||||
|
||||
async function test(context, commands) {
|
||||
// This violates all sorts of abstraction boundaries, but I don't see supported APIs for "just
|
||||
// waiting" nor for allowing navigation scripts to produce measurements.
|
||||
await commands.measure.start();
|
||||
|
@ -19,4 +21,25 @@ module.exports = async function(context, commands) {
|
|||
console.log("processLaunchToNavStart: " + processLaunchToNavStart);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
test,
|
||||
owner: "Performance Team",
|
||||
description: "Measures cold process applink time",
|
||||
long_description: `
|
||||
This test launches the appropriate android app, simulating an app link
|
||||
workflow. The application is launched with the intent action
|
||||
android.intent.action.VIEW loading a trivially simple website. The reported
|
||||
metric is the time from process start to navigationStart, reported as processLaunchToNavStart
|
||||
`,
|
||||
usage: `
|
||||
./mach perftest testing/performance/perftest_applink.js \
|
||||
--android-install-apk ~/fenix.v2.fennec-nightly.2020.04.22-arm32.apk \
|
||||
--hooks testing/performance/hooks_applink.py \
|
||||
--android-app-name org.mozilla.fennec_aurora \
|
||||
--perfherder-metrics processLaunchToNavStart
|
||||
`,
|
||||
supported_browser: ["Fenix nightly", "Geckoview_example", "Fennec"],
|
||||
platform: ["Android"],
|
||||
};
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
Metadata-Version: 1.1
|
||||
Name: esprima
|
||||
Version: 4.0.1
|
||||
Summary: ECMAScript parsing infrastructure for multipurpose analysis in Python
|
||||
Home-page: https://github.com/Kronuz/esprima-python
|
||||
Author: German M. Bravo (Kronuz)
|
||||
Author-email: german.mb@gmail.com
|
||||
License: BSD License
|
||||
Description: |Donate| |PyPI Version| |PyPI License| |PyPI Format| |PyPI Status|
|
||||
|
||||
**Esprima** (`esprima.org <http://esprima.org>`__, BSD license) is a
|
||||
high performance, standard-compliant
|
||||
`ECMAScript <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`__
|
||||
parser officially written in ECMAScript (also popularly known as
|
||||
`JavaScript <https://en.wikipedia.org/wiki/JavaScript>`__) and ported to
|
||||
Python. Esprima is created and maintained by `Ariya
|
||||
Hidayat <https://twitter.com/ariyahidayat>`__, with the help of `many
|
||||
contributors <https://github.com/jquery/esprima/contributors>`__.
|
||||
|
||||
Python port is a line-by-line manual translation and was created and is
|
||||
maintained by `German Mendez Bravo
|
||||
(Kronuz) <https://twitter.com/germbravo>`__.
|
||||
|
||||
Features
|
||||
~~~~~~~~
|
||||
|
||||
- Full support for ECMAScript 2017 (`ECMA-262 8th
|
||||
Edition <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`__)
|
||||
- Sensible `syntax tree
|
||||
format <https://github.com/estree/estree/blob/master/es5.md>`__ as
|
||||
standardized by `ESTree project <https://github.com/estree/estree>`__
|
||||
- Experimental support for `JSX <https://facebook.github.io/jsx/>`__, a
|
||||
syntax extension for `React <https://facebook.github.io/react/>`__
|
||||
- Optional tracking of syntax node location (index-based and
|
||||
line-column)
|
||||
- `Heavily tested <http://esprima.org/test/ci.html>`__ (~1500 `unit
|
||||
tests <https://github.com/jquery/esprima/tree/master/test/fixtures>`__
|
||||
with `full code
|
||||
coverage <https://codecov.io/github/jquery/esprima>`__)
|
||||
|
||||
Installation
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. code:: shell
|
||||
|
||||
pip install esprima
|
||||
|
||||
API
|
||||
~~~
|
||||
|
||||
Esprima can be used to perform `lexical
|
||||
analysis <https://en.wikipedia.org/wiki/Lexical_analysis>`__
|
||||
(tokenization) or `syntactic
|
||||
analysis <https://en.wikipedia.org/wiki/Parsing>`__ (parsing) of a
|
||||
JavaScript program.
|
||||
|
||||
A simple example:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
>>> import esprima
|
||||
>>> program = 'const answer = 42'
|
||||
|
||||
>>> esprima.tokenize(program)
|
||||
[{
|
||||
type: "Keyword",
|
||||
value: "const"
|
||||
}, {
|
||||
type: "Identifier",
|
||||
value: "answer"
|
||||
}, {
|
||||
type: "Punctuator",
|
||||
value: "="
|
||||
}, {
|
||||
type: "Numeric",
|
||||
value: "42"
|
||||
}]
|
||||
|
||||
>>> esprima.parseScript(program)
|
||||
{
|
||||
body: [
|
||||
{
|
||||
kind: "const",
|
||||
declarations: [
|
||||
{
|
||||
init: {
|
||||
raw: "42",
|
||||
type: "Literal",
|
||||
value: 42
|
||||
},
|
||||
type: "VariableDeclarator",
|
||||
id: {
|
||||
type: "Identifier",
|
||||
name: "answer"
|
||||
}
|
||||
}
|
||||
],
|
||||
type: "VariableDeclaration"
|
||||
}
|
||||
],
|
||||
type: "Program",
|
||||
sourceType: "script"
|
||||
}
|
||||
|
||||
For more information, please read the `complete
|
||||
documentation <http://esprima.org/doc>`__.
|
||||
|
||||
.. |Donate| image:: https://img.shields.io/badge/Donate-PayPal-green.svg
|
||||
:target: https://www.paypal.me/Kronuz/25
|
||||
.. |PyPI Version| image:: https://img.shields.io/pypi/v/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI License| image:: https://img.shields.io/pypi/l/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Wheel| image:: https://img.shields.io/pypi/wheel/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Format| image:: https://img.shields.io/pypi/format/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Python Version| image:: https://img.shields.io/pypi/pyversions/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Implementation| image:: https://img.shields.io/pypi/implementation/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Status| image:: https://img.shields.io/pypi/status/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Downloads| image:: https://img.shields.io/pypi/dm/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
Keywords: esprima ecmascript javascript parser ast
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Topic :: Software Development :: Code Generators
|
||||
Classifier: Topic :: Software Development :: Compilers
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Text Processing :: General
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
|
@ -0,0 +1,117 @@
|
|||
|Donate| |PyPI Version| |PyPI License| |PyPI Format| |PyPI Status|
|
||||
|
||||
**Esprima** (`esprima.org <http://esprima.org>`__, BSD license) is a
|
||||
high performance, standard-compliant
|
||||
`ECMAScript <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`__
|
||||
parser officially written in ECMAScript (also popularly known as
|
||||
`JavaScript <https://en.wikipedia.org/wiki/JavaScript>`__) and ported to
|
||||
Python. Esprima is created and maintained by `Ariya
|
||||
Hidayat <https://twitter.com/ariyahidayat>`__, with the help of `many
|
||||
contributors <https://github.com/jquery/esprima/contributors>`__.
|
||||
|
||||
Python port is a line-by-line manual translation and was created and is
|
||||
maintained by `German Mendez Bravo
|
||||
(Kronuz) <https://twitter.com/germbravo>`__.
|
||||
|
||||
Features
|
||||
~~~~~~~~
|
||||
|
||||
- Full support for ECMAScript 2017 (`ECMA-262 8th
|
||||
Edition <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`__)
|
||||
- Sensible `syntax tree
|
||||
format <https://github.com/estree/estree/blob/master/es5.md>`__ as
|
||||
standardized by `ESTree project <https://github.com/estree/estree>`__
|
||||
- Experimental support for `JSX <https://facebook.github.io/jsx/>`__, a
|
||||
syntax extension for `React <https://facebook.github.io/react/>`__
|
||||
- Optional tracking of syntax node location (index-based and
|
||||
line-column)
|
||||
- `Heavily tested <http://esprima.org/test/ci.html>`__ (~1500 `unit
|
||||
tests <https://github.com/jquery/esprima/tree/master/test/fixtures>`__
|
||||
with `full code
|
||||
coverage <https://codecov.io/github/jquery/esprima>`__)
|
||||
|
||||
Installation
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. code:: shell
|
||||
|
||||
pip install esprima
|
||||
|
||||
API
|
||||
~~~
|
||||
|
||||
Esprima can be used to perform `lexical
|
||||
analysis <https://en.wikipedia.org/wiki/Lexical_analysis>`__
|
||||
(tokenization) or `syntactic
|
||||
analysis <https://en.wikipedia.org/wiki/Parsing>`__ (parsing) of a
|
||||
JavaScript program.
|
||||
|
||||
A simple example:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
>>> import esprima
|
||||
>>> program = 'const answer = 42'
|
||||
|
||||
>>> esprima.tokenize(program)
|
||||
[{
|
||||
type: "Keyword",
|
||||
value: "const"
|
||||
}, {
|
||||
type: "Identifier",
|
||||
value: "answer"
|
||||
}, {
|
||||
type: "Punctuator",
|
||||
value: "="
|
||||
}, {
|
||||
type: "Numeric",
|
||||
value: "42"
|
||||
}]
|
||||
|
||||
>>> esprima.parseScript(program)
|
||||
{
|
||||
body: [
|
||||
{
|
||||
kind: "const",
|
||||
declarations: [
|
||||
{
|
||||
init: {
|
||||
raw: "42",
|
||||
type: "Literal",
|
||||
value: 42
|
||||
},
|
||||
type: "VariableDeclarator",
|
||||
id: {
|
||||
type: "Identifier",
|
||||
name: "answer"
|
||||
}
|
||||
}
|
||||
],
|
||||
type: "VariableDeclaration"
|
||||
}
|
||||
],
|
||||
type: "Program",
|
||||
sourceType: "script"
|
||||
}
|
||||
|
||||
For more information, please read the `complete
|
||||
documentation <http://esprima.org/doc>`__.
|
||||
|
||||
.. |Donate| image:: https://img.shields.io/badge/Donate-PayPal-green.svg
|
||||
:target: https://www.paypal.me/Kronuz/25
|
||||
.. |PyPI Version| image:: https://img.shields.io/pypi/v/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI License| image:: https://img.shields.io/pypi/l/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Wheel| image:: https://img.shields.io/pypi/wheel/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Format| image:: https://img.shields.io/pypi/format/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Python Version| image:: https://img.shields.io/pypi/pyversions/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Implementation| image:: https://img.shields.io/pypi/implementation/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Status| image:: https://img.shields.io/pypi/status/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
||||
.. |PyPI Downloads| image:: https://img.shields.io/pypi/dm/esprima.svg
|
||||
:target: https://pypi.python.org/pypi/esprima
|
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
version = '4.0.1'
|
||||
__version__ = (4, 0, 1)
|
||||
|
||||
from .esprima import * # NOQA
|
|
@ -0,0 +1,105 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals, print_function, division
|
||||
|
||||
import sys
|
||||
|
||||
from .esprima import parse, tokenize, Error, toDict
|
||||
from . import version
|
||||
|
||||
|
||||
def main():
|
||||
import json
|
||||
import time
|
||||
import optparse
|
||||
|
||||
usage = "usage: %prog [options] [file.js]"
|
||||
parser = optparse.OptionParser(usage=usage, version=version)
|
||||
parser.add_option("--comment", dest="comment",
|
||||
action="store_true", default=False,
|
||||
help="Gather all line and block comments in an array")
|
||||
parser.add_option("--attachComment", dest="attachComment",
|
||||
action="store_true", default=False,
|
||||
help="Attach comments to nodes")
|
||||
parser.add_option("--loc", dest="loc", default=False,
|
||||
action="store_true",
|
||||
help="Include line-column location info for each syntax node")
|
||||
parser.add_option("--range", dest="range", default=False,
|
||||
action="store_true",
|
||||
help="Include index-based range for each syntax node")
|
||||
parser.add_option("--raw", dest="raw", default=False,
|
||||
action="store_true",
|
||||
help="Display the raw value of literals")
|
||||
parser.add_option("--tokens", dest="tokens", default=False,
|
||||
action="store_true",
|
||||
help="List all tokens in an array")
|
||||
parser.add_option("--tolerant", dest="tolerant", default=False,
|
||||
action="store_true",
|
||||
help="Tolerate errors on a best-effort basis (experimental)")
|
||||
parser.add_option("--tokenize", dest="tokenize", default=False,
|
||||
action="store_true",
|
||||
help="Only tokenize, do not parse.")
|
||||
parser.add_option("--module", dest="sourceType", default='string',
|
||||
action="store_const", const='module',
|
||||
help="Tolerate errors on a best-effort basis (experimental)")
|
||||
parser.set_defaults(jsx=True, classProperties=True)
|
||||
opts, args = parser.parse_args()
|
||||
|
||||
if len(args) == 1:
|
||||
with open(args[0], 'rb') as f:
|
||||
code = f.read().decode('utf-8')
|
||||
elif sys.stdin.isatty():
|
||||
parser.print_help()
|
||||
return 64
|
||||
else:
|
||||
code = sys.stdin.read().decode('utf-8')
|
||||
|
||||
options = opts.__dict__
|
||||
do_tokenize = options.pop('tokenize')
|
||||
|
||||
t = time.time()
|
||||
try:
|
||||
if do_tokenize:
|
||||
del options['sourceType']
|
||||
del options['tokens']
|
||||
del options['raw']
|
||||
del options['jsx']
|
||||
res = toDict(tokenize(code, options=options))
|
||||
else:
|
||||
res = toDict(parse(code, options=options))
|
||||
except Error as e:
|
||||
res = e.toDict()
|
||||
dt = time.time() - t + 0.000000001
|
||||
|
||||
print(json.dumps(res, indent=4))
|
||||
print()
|
||||
print('Parsed everyting in', round(dt, 5), 'seconds.')
|
||||
print('Thats %d characters per second' % (len(code) // dt))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
retval = main()
|
||||
sys.exit(retval)
|
|
@ -0,0 +1,125 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import sys
|
||||
|
||||
import unicodedata
|
||||
from collections import defaultdict
|
||||
|
||||
from .compat import uchr, xrange
|
||||
|
||||
# http://stackoverflow.com/questions/14245893/efficiently-list-all-characters-in-a-given-unicode-category
|
||||
U_CATEGORIES = defaultdict(list)
|
||||
for c in map(uchr, xrange(sys.maxunicode + 1)):
|
||||
U_CATEGORIES[unicodedata.category(c)].append(c)
|
||||
UNICODE_LETTER = set(
|
||||
U_CATEGORIES['Lu'] + U_CATEGORIES['Ll'] +
|
||||
U_CATEGORIES['Lt'] + U_CATEGORIES['Lm'] +
|
||||
U_CATEGORIES['Lo'] + U_CATEGORIES['Nl']
|
||||
)
|
||||
UNICODE_OTHER_ID_START = set((
|
||||
# Other_ID_Start
|
||||
'\u1885', '\u1886', '\u2118', '\u212E', '\u309B', '\u309C',
|
||||
# New in Unicode 8.0
|
||||
'\u08B3', '\u0AF9', '\u13F8', '\u9FCD', '\uAB60', '\U00010CC0', '\U000108E0', '\U0002B820',
|
||||
# New in Unicode 9.0
|
||||
'\u1C80', '\U000104DB', '\U0001E922',
|
||||
'\U0001EE00', '\U0001EE06', '\U0001EE0A',
|
||||
))
|
||||
UNICODE_OTHER_ID_CONTINUE = set((
|
||||
# Other_ID_Continue
|
||||
'\xB7', '\u0387', '\u1369', '\u136A', '\u136B', '\u136C',
|
||||
'\u136D', '\u136E', '\u136F', '\u1370', '\u1371', '\u19DA',
|
||||
# New in Unicode 8.0
|
||||
'\u08E3', '\uA69E', '\U00011730',
|
||||
# New in Unicode 9.0
|
||||
'\u08D4', '\u1DFB', '\uA8C5', '\U00011450',
|
||||
'\U0001EE03', '\U0001EE0B',
|
||||
))
|
||||
UNICODE_COMBINING_MARK = set(U_CATEGORIES['Mn'] + U_CATEGORIES['Mc'])
|
||||
UNICODE_DIGIT = set(U_CATEGORIES['Nd'])
|
||||
UNICODE_CONNECTOR_PUNCTUATION = set(U_CATEGORIES['Pc'])
|
||||
IDENTIFIER_START = UNICODE_LETTER.union(UNICODE_OTHER_ID_START).union(set(('$', '_', '\\')))
|
||||
IDENTIFIER_PART = IDENTIFIER_START.union(UNICODE_COMBINING_MARK).union(UNICODE_DIGIT).union(UNICODE_CONNECTOR_PUNCTUATION).union(set(('\u200D', '\u200C'))).union(UNICODE_OTHER_ID_CONTINUE)
|
||||
|
||||
WHITE_SPACE = set((
|
||||
'\x09', '\x0B', '\x0C', '\x20', '\xA0',
|
||||
'\u1680', '\u180E', '\u2000', '\u2001', '\u2002',
|
||||
'\u2003', '\u2004', '\u2005', '\u2006', '\u2007',
|
||||
'\u2008', '\u2009', '\u200A', '\u202F', '\u205F',
|
||||
'\u3000', '\uFEFF',
|
||||
))
|
||||
LINE_TERMINATOR = set(('\x0A', '\x0D', '\u2028', '\u2029'))
|
||||
|
||||
DECIMAL_CONV = dict((c, n) for n, c in enumerate('0123456789'))
|
||||
OCTAL_CONV = dict((c, n) for n, c in enumerate('01234567'))
|
||||
HEX_CONV = dict((c, n) for n, c in enumerate('0123456789abcdef'))
|
||||
for n, c in enumerate('ABCDEF', 10):
|
||||
HEX_CONV[c] = n
|
||||
DECIMAL_DIGIT = set(DECIMAL_CONV.keys())
|
||||
OCTAL_DIGIT = set(OCTAL_CONV.keys())
|
||||
HEX_DIGIT = set(HEX_CONV.keys())
|
||||
|
||||
|
||||
class Character:
|
||||
@staticmethod
|
||||
def fromCodePoint(code):
|
||||
return uchr(code)
|
||||
|
||||
# https://tc39.github.io/ecma262/#sec-white-space
|
||||
|
||||
@staticmethod
|
||||
def isWhiteSpace(ch):
|
||||
return ch in WHITE_SPACE
|
||||
|
||||
# https://tc39.github.io/ecma262/#sec-line-terminators
|
||||
|
||||
@staticmethod
|
||||
def isLineTerminator(ch):
|
||||
return ch in LINE_TERMINATOR
|
||||
|
||||
# https://tc39.github.io/ecma262/#sec-names-and-keywords
|
||||
|
||||
@staticmethod
|
||||
def isIdentifierStart(ch):
|
||||
return ch in IDENTIFIER_START
|
||||
|
||||
@staticmethod
|
||||
def isIdentifierPart(ch):
|
||||
return ch in IDENTIFIER_PART
|
||||
|
||||
# https://tc39.github.io/ecma262/#sec-literals-numeric-literals
|
||||
|
||||
@staticmethod
|
||||
def isDecimalDigit(ch):
|
||||
return ch in DECIMAL_DIGIT
|
||||
|
||||
@staticmethod
|
||||
def isHexDigit(ch):
|
||||
return ch in HEX_DIGIT
|
||||
|
||||
@staticmethod
|
||||
def isOctalDigit(ch):
|
||||
return ch in OCTAL_DIGIT
|
|
@ -0,0 +1,176 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, self.list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, self.list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# self.SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES
|
||||
# LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# self.SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .objects import Object
|
||||
from .nodes import Node
|
||||
from .syntax import Syntax
|
||||
|
||||
|
||||
class Comment(Node):
|
||||
def __init__(self, type, value, range=None, loc=None):
|
||||
self.type = type
|
||||
self.value = value
|
||||
self.range = range
|
||||
self.loc = loc
|
||||
|
||||
|
||||
class Entry(Object):
|
||||
def __init__(self, comment, start):
|
||||
self.comment = comment
|
||||
self.start = start
|
||||
|
||||
|
||||
class NodeInfo(Object):
|
||||
def __init__(self, node, start):
|
||||
self.node = node
|
||||
self.start = start
|
||||
|
||||
|
||||
class CommentHandler(object):
|
||||
def __init__(self):
|
||||
self.attach = False
|
||||
self.comments = []
|
||||
self.stack = []
|
||||
self.leading = []
|
||||
self.trailing = []
|
||||
|
||||
def insertInnerComments(self, node, metadata):
|
||||
# innnerComments for properties empty block
|
||||
# `function a(:/** comments **\/}`
|
||||
if node.type is Syntax.BlockStatement and not node.body:
|
||||
innerComments = []
|
||||
for i, entry in enumerate(self.leading):
|
||||
if metadata.end.offset >= entry.start:
|
||||
innerComments.append(entry.comment)
|
||||
self.leading[i] = None
|
||||
self.trailing[i] = None
|
||||
if innerComments:
|
||||
node.innerComments = innerComments
|
||||
self.leading = [v for v in self.leading if v is not None]
|
||||
self.trailing = [v for v in self.trailing if v is not None]
|
||||
|
||||
def findTrailingComments(self, metadata):
|
||||
trailingComments = []
|
||||
|
||||
if self.trailing:
|
||||
for i, entry in enumerate(self.trailing):
|
||||
if entry.start >= metadata.end.offset:
|
||||
trailingComments.append(entry.comment)
|
||||
if trailingComments:
|
||||
self.trailing = []
|
||||
return trailingComments
|
||||
|
||||
last = self.stack and self.stack[-1]
|
||||
if last and last.node.trailingComments:
|
||||
firstComment = last.node.trailingComments[0]
|
||||
if firstComment and firstComment.range[0] >= metadata.end.offset:
|
||||
trailingComments = last.node.trailingComments
|
||||
del last.node.trailingComments
|
||||
return trailingComments
|
||||
|
||||
def findLeadingComments(self, metadata):
|
||||
leadingComments = []
|
||||
|
||||
target = None
|
||||
while self.stack:
|
||||
entry = self.stack and self.stack[-1]
|
||||
if entry and entry.start >= metadata.start.offset:
|
||||
target = entry.node
|
||||
self.stack.pop()
|
||||
else:
|
||||
break
|
||||
|
||||
if target:
|
||||
if target.leadingComments:
|
||||
for i, comment in enumerate(target.leadingComments):
|
||||
if comment.range[1] <= metadata.start.offset:
|
||||
leadingComments.append(comment)
|
||||
target.leadingComments[i] = None
|
||||
if leadingComments:
|
||||
target.leadingComments = [v for v in target.leadingComments if v is not None]
|
||||
if not target.leadingComments:
|
||||
del target.leadingComments
|
||||
return leadingComments
|
||||
|
||||
for i, entry in enumerate(self.leading):
|
||||
if entry.start <= metadata.start.offset:
|
||||
leadingComments.append(entry.comment)
|
||||
self.leading[i] = None
|
||||
if leadingComments:
|
||||
self.leading = [v for v in self.leading if v is not None]
|
||||
|
||||
return leadingComments
|
||||
|
||||
def visitNode(self, node, metadata):
|
||||
if node.type is Syntax.Program and node.body:
|
||||
return
|
||||
|
||||
self.insertInnerComments(node, metadata)
|
||||
trailingComments = self.findTrailingComments(metadata)
|
||||
leadingComments = self.findLeadingComments(metadata)
|
||||
if leadingComments:
|
||||
node.leadingComments = leadingComments
|
||||
if trailingComments:
|
||||
node.trailingComments = trailingComments
|
||||
|
||||
self.stack.append(NodeInfo(
|
||||
node=node,
|
||||
start=metadata.start.offset
|
||||
))
|
||||
|
||||
def visitComment(self, node, metadata):
|
||||
type = 'Line' if node.type[0] == 'L' else 'Block'
|
||||
comment = Comment(
|
||||
type=type,
|
||||
value=node.value
|
||||
)
|
||||
if node.range:
|
||||
comment.range = node.range
|
||||
if node.loc:
|
||||
comment.loc = node.loc
|
||||
self.comments.append(comment)
|
||||
|
||||
if self.attach:
|
||||
entry = Entry(
|
||||
comment=Comment(
|
||||
type=type,
|
||||
value=node.value,
|
||||
range=[metadata.start.offset, metadata.end.offset]
|
||||
),
|
||||
start=metadata.start.offset
|
||||
)
|
||||
if node.loc:
|
||||
entry.comment.loc = node.loc
|
||||
node.type = type
|
||||
self.leading.append(entry)
|
||||
self.trailing.append(entry)
|
||||
|
||||
def visit(self, node, metadata):
|
||||
if node.type == 'LineComment':
|
||||
self.visitComment(node, metadata)
|
||||
elif node.type == 'BlockComment':
|
||||
self.visitComment(node, metadata)
|
||||
elif self.attach:
|
||||
self.visitNode(node, metadata)
|
|
@ -0,0 +1,72 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import sys
|
||||
|
||||
PY3 = sys.version_info >= (3, 0)
|
||||
|
||||
if PY3:
|
||||
# Python 3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
uchr = chr
|
||||
|
||||
def uord(ch):
|
||||
return ord(ch[0])
|
||||
|
||||
else:
|
||||
basestring = basestring
|
||||
long = long
|
||||
xrange = xrange
|
||||
unicode = unicode
|
||||
|
||||
try:
|
||||
# Python 2 UCS4:
|
||||
unichr(0x10000)
|
||||
uchr = unichr
|
||||
|
||||
def uord(ch):
|
||||
return ord(ch[0])
|
||||
|
||||
except ValueError:
|
||||
# Python 2 UCS2:
|
||||
def uchr(code):
|
||||
# UTF-16 Encoding
|
||||
if code <= 0xFFFF:
|
||||
return unichr(code)
|
||||
cu1 = ((code - 0x10000) >> 10) + 0xD800
|
||||
cu2 = ((code - 0x10000) & 1023) + 0xDC00
|
||||
return unichr(cu1) + unichr(cu2)
|
||||
|
||||
def uord(ch):
|
||||
cp = ord(ch[0])
|
||||
if cp >= 0xD800 and cp <= 0xDBFF:
|
||||
second = ord(ch[1])
|
||||
if second >= 0xDC00 and second <= 0xDFFF:
|
||||
first = cp
|
||||
cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000
|
||||
return cp
|
|
@ -0,0 +1,74 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .compat import unicode
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
def __init__(self, message, name=None, index=None, lineNumber=None, column=None, description=None):
|
||||
super(Error, self).__init__(message)
|
||||
self.message = message
|
||||
self.name = name
|
||||
self.index = index
|
||||
self.lineNumber = lineNumber
|
||||
self.column = column
|
||||
# self.description = description
|
||||
|
||||
def toString(self):
|
||||
return '%s: %s' % (self.__class__.__name__, self)
|
||||
|
||||
def toDict(self):
|
||||
d = dict((unicode(k), v) for k, v in self.__dict__.items() if v is not None)
|
||||
d['message'] = self.toString()
|
||||
return d
|
||||
|
||||
|
||||
class ErrorHandler:
|
||||
def __init__(self):
|
||||
self.errors = []
|
||||
self.tolerant = False
|
||||
|
||||
def recordError(self, error):
|
||||
self.errors.append(error.toDict())
|
||||
|
||||
def tolerate(self, error):
|
||||
if self.tolerant:
|
||||
self.recordError(error)
|
||||
else:
|
||||
raise error
|
||||
|
||||
def createError(self, index, line, col, description):
|
||||
msg = 'Line %s: %s' % (line, description)
|
||||
return Error(msg, index=index, lineNumber=line, column=col, description=description)
|
||||
|
||||
def throwError(self, index, line, col, description):
|
||||
raise self.createError(index, line, col, description)
|
||||
|
||||
def tolerateError(self, index, line, col, description):
|
||||
error = self.createError(index, line, col, description)
|
||||
if self.tolerant:
|
||||
self.recordError(error)
|
||||
else:
|
||||
raise error
|
|
@ -0,0 +1,125 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .comment_handler import CommentHandler
|
||||
from .error_handler import Error
|
||||
from .jsx_parser import JSXParser
|
||||
from .jsx_syntax import JSXSyntax
|
||||
from .objects import Array, toDict
|
||||
from .parser import Parser
|
||||
from .syntax import Syntax
|
||||
from .tokenizer import Tokenizer
|
||||
from .visitor import NodeVisitor
|
||||
from . import nodes
|
||||
from . import jsx_nodes
|
||||
|
||||
|
||||
__all__ = ['Syntax', 'JSXSyntax', 'Error', 'NodeVisitor', 'nodes', 'jsx_nodes',
|
||||
'parse', 'parseModule', 'parseScript', 'tokenize', 'toDict']
|
||||
|
||||
|
||||
def parse(code, options=None, delegate=None, **kwargs):
|
||||
options = {} if options is None else options.copy()
|
||||
options.update(kwargs)
|
||||
|
||||
# ESNext presset:
|
||||
if options.get('esnext', False):
|
||||
options['jsx'] = True
|
||||
options['classProperties'] = True
|
||||
|
||||
commentHandler = None
|
||||
|
||||
def proxyDelegate(node, metadata):
|
||||
if delegate:
|
||||
new_node = delegate(node, metadata)
|
||||
if new_node is not None:
|
||||
node = new_node
|
||||
if commentHandler:
|
||||
commentHandler.visit(node, metadata)
|
||||
return node
|
||||
|
||||
parserDelegate = None if delegate is None else proxyDelegate
|
||||
collectComment = options.get('comment', False)
|
||||
attachComment = options.get('attachComment', False)
|
||||
if collectComment or attachComment:
|
||||
commentHandler = CommentHandler()
|
||||
commentHandler.attach = attachComment
|
||||
options['comment'] = True
|
||||
parserDelegate = proxyDelegate
|
||||
|
||||
isModule = options.get('sourceType', 'script') == 'module'
|
||||
|
||||
if options.get('jsx', False):
|
||||
parser = JSXParser(code, options=options, delegate=parserDelegate)
|
||||
else:
|
||||
parser = Parser(code, options=options, delegate=parserDelegate)
|
||||
|
||||
ast = parser.parseModule() if isModule else parser.parseScript()
|
||||
|
||||
if collectComment and commentHandler:
|
||||
ast.comments = commentHandler.comments
|
||||
|
||||
if parser.config.tokens:
|
||||
ast.tokens = parser.tokens
|
||||
|
||||
if parser.config.tolerant:
|
||||
ast.errors = parser.errorHandler.errors
|
||||
|
||||
return ast
|
||||
|
||||
|
||||
def parseModule(code, options=None, delegate=None, **kwargs):
|
||||
kwargs['sourceType'] = 'module'
|
||||
return parse(code, options, delegate, **kwargs)
|
||||
|
||||
|
||||
def parseScript(code, options=None, delegate=None, **kwargs):
|
||||
kwargs['sourceType'] = 'script'
|
||||
return parse(code, options, delegate, **kwargs)
|
||||
|
||||
|
||||
def tokenize(code, options=None, delegate=None, **kwargs):
|
||||
options = {} if options is None else options.copy()
|
||||
options.update(kwargs)
|
||||
|
||||
tokenizer = Tokenizer(code, options)
|
||||
|
||||
tokens = Array()
|
||||
|
||||
try:
|
||||
while True:
|
||||
token = tokenizer.getNextToken()
|
||||
if not token:
|
||||
break
|
||||
if delegate:
|
||||
token = delegate(token)
|
||||
tokens.append(token)
|
||||
except Error as e:
|
||||
tokenizer.errorHandler.tolerate(e)
|
||||
|
||||
if tokenizer.errorHandler.tolerant:
|
||||
tokens.errors = tokenizer.errors()
|
||||
|
||||
return tokens
|
|
@ -0,0 +1,100 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .nodes import Node
|
||||
from .jsx_syntax import JSXSyntax
|
||||
|
||||
|
||||
class JSXClosingElement(Node):
|
||||
def __init__(self, name):
|
||||
self.type = JSXSyntax.JSXClosingElement
|
||||
self.name = name
|
||||
|
||||
|
||||
class JSXElement(Node):
|
||||
def __init__(self, openingElement, children, closingElement):
|
||||
self.type = JSXSyntax.JSXElement
|
||||
self.openingElement = openingElement
|
||||
self.children = children
|
||||
self.closingElement = closingElement
|
||||
|
||||
|
||||
class JSXEmptyExpression(Node):
|
||||
def __init__(self):
|
||||
self.type = JSXSyntax.JSXEmptyExpression
|
||||
|
||||
|
||||
class JSXExpressionContainer(Node):
|
||||
def __init__(self, expression):
|
||||
self.type = JSXSyntax.JSXExpressionContainer
|
||||
self.expression = expression
|
||||
|
||||
|
||||
class JSXIdentifier(Node):
|
||||
def __init__(self, name):
|
||||
self.type = JSXSyntax.JSXIdentifier
|
||||
self.name = name
|
||||
|
||||
|
||||
class JSXMemberExpression(Node):
|
||||
def __init__(self, object, property):
|
||||
self.type = JSXSyntax.JSXMemberExpression
|
||||
self.object = object
|
||||
self.property = property
|
||||
|
||||
|
||||
class JSXAttribute(Node):
|
||||
def __init__(self, name, value):
|
||||
self.type = JSXSyntax.JSXAttribute
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
|
||||
class JSXNamespacedName(Node):
|
||||
def __init__(self, namespace, name):
|
||||
self.type = JSXSyntax.JSXNamespacedName
|
||||
self.namespace = namespace
|
||||
self.name = name
|
||||
|
||||
|
||||
class JSXOpeningElement(Node):
|
||||
def __init__(self, name, selfClosing, attributes):
|
||||
self.type = JSXSyntax.JSXOpeningElement
|
||||
self.name = name
|
||||
self.selfClosing = selfClosing
|
||||
self.attributes = attributes
|
||||
|
||||
|
||||
class JSXSpreadAttribute(Node):
|
||||
def __init__(self, argument):
|
||||
self.type = JSXSyntax.JSXSpreadAttribute
|
||||
self.argument = argument
|
||||
|
||||
|
||||
class JSXText(Node):
|
||||
def __init__(self, value, raw):
|
||||
self.type = JSXSyntax.JSXText
|
||||
self.value = value
|
||||
self.raw = raw
|
|
@ -0,0 +1,584 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .compat import uchr
|
||||
from .character import Character
|
||||
from . import jsx_nodes as JSXNode
|
||||
from .jsx_syntax import JSXSyntax
|
||||
from . import nodes as Node
|
||||
from .parser import Marker, Parser
|
||||
from .token import Token, TokenName
|
||||
from .xhtml_entities import XHTMLEntities
|
||||
|
||||
|
||||
class MetaJSXElement(object):
|
||||
def __init__(self, node=None, opening=None, closing=None, children=None):
|
||||
self.node = node
|
||||
self.opening = opening
|
||||
self.closing = closing
|
||||
self.children = children
|
||||
|
||||
|
||||
class JSXToken(object):
|
||||
Identifier = 100
|
||||
Text = 101
|
||||
|
||||
|
||||
class RawJSXToken(object):
|
||||
def __init__(self, type=None, value=None, lineNumber=None, lineStart=None, start=None, end=None):
|
||||
self.type = type
|
||||
self.value = value
|
||||
self.lineNumber = lineNumber
|
||||
self.lineStart = lineStart
|
||||
self.start = start
|
||||
self.end = end
|
||||
|
||||
|
||||
TokenName[JSXToken.Identifier] = "JSXIdentifier"
|
||||
TokenName[JSXToken.Text] = "JSXText"
|
||||
|
||||
|
||||
# Fully qualified element name, e.g. <svg:path> returns "svg:path"
|
||||
def getQualifiedElementName(elementName):
|
||||
typ = elementName.type
|
||||
if typ is JSXSyntax.JSXIdentifier:
|
||||
id = elementName
|
||||
qualifiedName = id.name
|
||||
elif typ is JSXSyntax.JSXNamespacedName:
|
||||
ns = elementName
|
||||
qualifiedName = getQualifiedElementName(ns.namespace) + ':' + getQualifiedElementName(ns.name)
|
||||
elif typ is JSXSyntax.JSXMemberExpression:
|
||||
expr = elementName
|
||||
qualifiedName = getQualifiedElementName(expr.object) + '.' + getQualifiedElementName(expr.property)
|
||||
|
||||
return qualifiedName
|
||||
|
||||
|
||||
class JSXParser(Parser):
|
||||
def __init__(self, code, options, delegate):
|
||||
super(JSXParser, self).__init__(code, options, delegate)
|
||||
|
||||
def parsePrimaryExpression(self):
|
||||
return self.parseJSXRoot() if self.match('<') else super(JSXParser, self).parsePrimaryExpression()
|
||||
|
||||
def startJSX(self):
|
||||
# Unwind the scanner before the lookahead token.
|
||||
self.scanner.index = self.startMarker.index
|
||||
self.scanner.lineNumber = self.startMarker.line
|
||||
self.scanner.lineStart = self.startMarker.index - self.startMarker.column
|
||||
|
||||
def finishJSX(self):
|
||||
# Prime the next lookahead.
|
||||
self.nextToken()
|
||||
|
||||
def reenterJSX(self):
|
||||
self.startJSX()
|
||||
self.expectJSX('}')
|
||||
|
||||
# Pop the closing '}' added from the lookahead.
|
||||
if self.config.tokens:
|
||||
self.tokens.pop()
|
||||
|
||||
def createJSXNode(self):
|
||||
self.collectComments()
|
||||
return Marker(
|
||||
index=self.scanner.index,
|
||||
line=self.scanner.lineNumber,
|
||||
column=self.scanner.index - self.scanner.lineStart
|
||||
)
|
||||
|
||||
def createJSXChildNode(self):
|
||||
return Marker(
|
||||
index=self.scanner.index,
|
||||
line=self.scanner.lineNumber,
|
||||
column=self.scanner.index - self.scanner.lineStart
|
||||
)
|
||||
|
||||
def scanXHTMLEntity(self, quote):
|
||||
result = '&'
|
||||
|
||||
valid = True
|
||||
terminated = False
|
||||
numeric = False
|
||||
hex = False
|
||||
|
||||
while not self.scanner.eof() and valid and not terminated:
|
||||
ch = self.scanner.source[self.scanner.index]
|
||||
if ch == quote:
|
||||
break
|
||||
|
||||
terminated = (ch == ';')
|
||||
result += ch
|
||||
self.scanner.index += 1
|
||||
if not terminated:
|
||||
length = len(result)
|
||||
if length == 2:
|
||||
# e.g. '{'
|
||||
numeric = (ch == '#')
|
||||
elif length == 3:
|
||||
if numeric:
|
||||
# e.g. 'A'
|
||||
hex = ch == 'x'
|
||||
valid = hex or Character.isDecimalDigit(ch)
|
||||
numeric = numeric and not hex
|
||||
else:
|
||||
valid = valid and not (numeric and not Character.isDecimalDigit(ch))
|
||||
valid = valid and not (hex and not Character.isHexDigit(ch))
|
||||
|
||||
if valid and terminated and len(result) > 2:
|
||||
# e.g. 'A' becomes just '#x41'
|
||||
st = result[1:-1]
|
||||
if numeric and len(st) > 1:
|
||||
result = uchr(int(st[1:], 10))
|
||||
elif hex and len(st) > 2:
|
||||
result = uchr(int(st[2:], 16))
|
||||
elif not numeric and not hex and st in XHTMLEntities:
|
||||
result = XHTMLEntities[st]
|
||||
|
||||
return result
|
||||
|
||||
# Scan the next JSX token. This replaces Scanner#lex when in JSX mode.
|
||||
|
||||
def lexJSX(self):
|
||||
ch = self.scanner.source[self.scanner.index]
|
||||
|
||||
# < > / : = { }
|
||||
if ch in ('<', '>', '/', ':', '=', '{', '}'):
|
||||
value = self.scanner.source[self.scanner.index]
|
||||
self.scanner.index += 1
|
||||
return RawJSXToken(
|
||||
type=Token.Punctuator,
|
||||
value=value,
|
||||
lineNumber=self.scanner.lineNumber,
|
||||
lineStart=self.scanner.lineStart,
|
||||
start=self.scanner.index - 1,
|
||||
end=self.scanner.index
|
||||
)
|
||||
|
||||
# " '
|
||||
if ch in ('\'', '"'):
|
||||
start = self.scanner.index
|
||||
quote = self.scanner.source[self.scanner.index]
|
||||
self.scanner.index += 1
|
||||
str = ''
|
||||
while not self.scanner.eof():
|
||||
ch = self.scanner.source[self.scanner.index]
|
||||
self.scanner.index += 1
|
||||
if ch == quote:
|
||||
break
|
||||
elif ch == '&':
|
||||
str += self.scanXHTMLEntity(quote)
|
||||
else:
|
||||
str += ch
|
||||
|
||||
return RawJSXToken(
|
||||
type=Token.StringLiteral,
|
||||
value=str,
|
||||
lineNumber=self.scanner.lineNumber,
|
||||
lineStart=self.scanner.lineStart,
|
||||
start=start,
|
||||
end=self.scanner.index
|
||||
)
|
||||
|
||||
# ... or .
|
||||
if ch == '.':
|
||||
start = self.scanner.index
|
||||
if self.scanner.source[start + 1:start + 3] == '..':
|
||||
value = '...'
|
||||
self.scanner.index += 3
|
||||
else:
|
||||
value = '.'
|
||||
self.scanner.index += 1
|
||||
return RawJSXToken(
|
||||
type=Token.Punctuator,
|
||||
value=value,
|
||||
lineNumber=self.scanner.lineNumber,
|
||||
lineStart=self.scanner.lineStart,
|
||||
start=start,
|
||||
end=self.scanner.index
|
||||
)
|
||||
|
||||
# `
|
||||
if ch == '`':
|
||||
# Only placeholder, since it will be rescanned as a real assignment expression.
|
||||
return RawJSXToken(
|
||||
type=Token.Template,
|
||||
value='',
|
||||
lineNumber=self.scanner.lineNumber,
|
||||
lineStart=self.scanner.lineStart,
|
||||
start=self.scanner.index,
|
||||
end=self.scanner.index
|
||||
)
|
||||
|
||||
# Identifer can not contain backslash (char code 92).
|
||||
if Character.isIdentifierStart(ch) and ch != '\\':
|
||||
start = self.scanner.index
|
||||
self.scanner.index += 1
|
||||
while not self.scanner.eof():
|
||||
ch = self.scanner.source[self.scanner.index]
|
||||
if Character.isIdentifierPart(ch) and ch != '\\':
|
||||
self.scanner.index += 1
|
||||
elif ch == '-':
|
||||
# Hyphen (char code 45) can be part of an identifier.
|
||||
self.scanner.index += 1
|
||||
else:
|
||||
break
|
||||
|
||||
id = self.scanner.source[start:self.scanner.index]
|
||||
return RawJSXToken(
|
||||
type=JSXToken.Identifier,
|
||||
value=id,
|
||||
lineNumber=self.scanner.lineNumber,
|
||||
lineStart=self.scanner.lineStart,
|
||||
start=start,
|
||||
end=self.scanner.index
|
||||
)
|
||||
|
||||
return self.scanner.lex()
|
||||
|
||||
def nextJSXToken(self):
|
||||
self.collectComments()
|
||||
|
||||
self.startMarker.index = self.scanner.index
|
||||
self.startMarker.line = self.scanner.lineNumber
|
||||
self.startMarker.column = self.scanner.index - self.scanner.lineStart
|
||||
token = self.lexJSX()
|
||||
self.lastMarker.index = self.scanner.index
|
||||
self.lastMarker.line = self.scanner.lineNumber
|
||||
self.lastMarker.column = self.scanner.index - self.scanner.lineStart
|
||||
|
||||
if self.config.tokens:
|
||||
self.tokens.append(self.convertToken(token))
|
||||
|
||||
return token
|
||||
|
||||
def nextJSXText(self):
|
||||
self.startMarker.index = self.scanner.index
|
||||
self.startMarker.line = self.scanner.lineNumber
|
||||
self.startMarker.column = self.scanner.index - self.scanner.lineStart
|
||||
|
||||
start = self.scanner.index
|
||||
|
||||
text = ''
|
||||
while not self.scanner.eof():
|
||||
ch = self.scanner.source[self.scanner.index]
|
||||
if ch in ('{', '<'):
|
||||
break
|
||||
|
||||
self.scanner.index += 1
|
||||
text += ch
|
||||
if Character.isLineTerminator(ch):
|
||||
self.scanner.lineNumber += 1
|
||||
if ch == '\r' and self.scanner.source[self.scanner.index] == '\n':
|
||||
self.scanner.index += 1
|
||||
|
||||
self.scanner.lineStart = self.scanner.index
|
||||
|
||||
self.lastMarker.index = self.scanner.index
|
||||
self.lastMarker.line = self.scanner.lineNumber
|
||||
self.lastMarker.column = self.scanner.index - self.scanner.lineStart
|
||||
|
||||
token = RawJSXToken(
|
||||
type=JSXToken.Text,
|
||||
value=text,
|
||||
lineNumber=self.scanner.lineNumber,
|
||||
lineStart=self.scanner.lineStart,
|
||||
start=start,
|
||||
end=self.scanner.index
|
||||
)
|
||||
|
||||
if text and self.config.tokens:
|
||||
self.tokens.append(self.convertToken(token))
|
||||
|
||||
return token
|
||||
|
||||
def peekJSXToken(self):
|
||||
state = self.scanner.saveState()
|
||||
self.scanner.scanComments()
|
||||
next = self.lexJSX()
|
||||
self.scanner.restoreState(state)
|
||||
|
||||
return next
|
||||
|
||||
# Expect the next JSX token to match the specified punctuator.
|
||||
# If not, an exception will be thrown.
|
||||
|
||||
def expectJSX(self, value):
|
||||
token = self.nextJSXToken()
|
||||
if token.type is not Token.Punctuator or token.value != value:
|
||||
self.throwUnexpectedToken(token)
|
||||
|
||||
# Return True if the next JSX token matches the specified punctuator.
|
||||
|
||||
def matchJSX(self, *value):
|
||||
next = self.peekJSXToken()
|
||||
return next.type is Token.Punctuator and next.value in value
|
||||
|
||||
def parseJSXIdentifier(self):
|
||||
node = self.createJSXNode()
|
||||
token = self.nextJSXToken()
|
||||
if token.type is not JSXToken.Identifier:
|
||||
self.throwUnexpectedToken(token)
|
||||
|
||||
return self.finalize(node, JSXNode.JSXIdentifier(token.value))
|
||||
|
||||
def parseJSXElementName(self):
|
||||
node = self.createJSXNode()
|
||||
elementName = self.parseJSXIdentifier()
|
||||
|
||||
if self.matchJSX(':'):
|
||||
namespace = elementName
|
||||
self.expectJSX(':')
|
||||
name = self.parseJSXIdentifier()
|
||||
elementName = self.finalize(node, JSXNode.JSXNamespacedName(namespace, name))
|
||||
elif self.matchJSX('.'):
|
||||
while self.matchJSX('.'):
|
||||
object = elementName
|
||||
self.expectJSX('.')
|
||||
property = self.parseJSXIdentifier()
|
||||
elementName = self.finalize(node, JSXNode.JSXMemberExpression(object, property))
|
||||
|
||||
return elementName
|
||||
|
||||
def parseJSXAttributeName(self):
|
||||
node = self.createJSXNode()
|
||||
|
||||
identifier = self.parseJSXIdentifier()
|
||||
if self.matchJSX(':'):
|
||||
namespace = identifier
|
||||
self.expectJSX(':')
|
||||
name = self.parseJSXIdentifier()
|
||||
attributeName = self.finalize(node, JSXNode.JSXNamespacedName(namespace, name))
|
||||
else:
|
||||
attributeName = identifier
|
||||
|
||||
return attributeName
|
||||
|
||||
def parseJSXStringLiteralAttribute(self):
|
||||
node = self.createJSXNode()
|
||||
token = self.nextJSXToken()
|
||||
if token.type is not Token.StringLiteral:
|
||||
self.throwUnexpectedToken(token)
|
||||
|
||||
raw = self.getTokenRaw(token)
|
||||
return self.finalize(node, Node.Literal(token.value, raw))
|
||||
|
||||
def parseJSXExpressionAttribute(self):
|
||||
node = self.createJSXNode()
|
||||
|
||||
self.expectJSX('{')
|
||||
self.finishJSX()
|
||||
|
||||
if self.match('}'):
|
||||
self.tolerateError('JSX attributes must only be assigned a non-empty expression')
|
||||
|
||||
expression = self.parseAssignmentExpression()
|
||||
self.reenterJSX()
|
||||
|
||||
return self.finalize(node, JSXNode.JSXExpressionContainer(expression))
|
||||
|
||||
def parseJSXAttributeValue(self):
|
||||
if self.matchJSX('{'):
|
||||
return self.parseJSXExpressionAttribute()
|
||||
if self.matchJSX('<'):
|
||||
return self.parseJSXElement()
|
||||
|
||||
return self.parseJSXStringLiteralAttribute()
|
||||
|
||||
def parseJSXNameValueAttribute(self):
|
||||
node = self.createJSXNode()
|
||||
name = self.parseJSXAttributeName()
|
||||
value = None
|
||||
if self.matchJSX('='):
|
||||
self.expectJSX('=')
|
||||
value = self.parseJSXAttributeValue()
|
||||
|
||||
return self.finalize(node, JSXNode.JSXAttribute(name, value))
|
||||
|
||||
def parseJSXSpreadAttribute(self):
|
||||
node = self.createJSXNode()
|
||||
self.expectJSX('{')
|
||||
self.expectJSX('...')
|
||||
|
||||
self.finishJSX()
|
||||
argument = self.parseAssignmentExpression()
|
||||
self.reenterJSX()
|
||||
|
||||
return self.finalize(node, JSXNode.JSXSpreadAttribute(argument))
|
||||
|
||||
def parseJSXAttributes(self):
|
||||
attributes = []
|
||||
|
||||
while not self.matchJSX('/', '>'):
|
||||
attribute = self.parseJSXSpreadAttribute() if self.matchJSX('{') else self.parseJSXNameValueAttribute()
|
||||
attributes.append(attribute)
|
||||
|
||||
return attributes
|
||||
|
||||
def parseJSXOpeningElement(self):
|
||||
node = self.createJSXNode()
|
||||
|
||||
self.expectJSX('<')
|
||||
name = self.parseJSXElementName()
|
||||
attributes = self.parseJSXAttributes()
|
||||
selfClosing = self.matchJSX('/')
|
||||
if selfClosing:
|
||||
self.expectJSX('/')
|
||||
|
||||
self.expectJSX('>')
|
||||
|
||||
return self.finalize(node, JSXNode.JSXOpeningElement(name, selfClosing, attributes))
|
||||
|
||||
def parseJSXBoundaryElement(self):
|
||||
node = self.createJSXNode()
|
||||
|
||||
self.expectJSX('<')
|
||||
if self.matchJSX('/'):
|
||||
self.expectJSX('/')
|
||||
elementName = self.parseJSXElementName()
|
||||
self.expectJSX('>')
|
||||
return self.finalize(node, JSXNode.JSXClosingElement(elementName))
|
||||
|
||||
name = self.parseJSXElementName()
|
||||
attributes = self.parseJSXAttributes()
|
||||
selfClosing = self.matchJSX('/')
|
||||
if selfClosing:
|
||||
self.expectJSX('/')
|
||||
|
||||
self.expectJSX('>')
|
||||
|
||||
return self.finalize(node, JSXNode.JSXOpeningElement(name, selfClosing, attributes))
|
||||
|
||||
def parseJSXEmptyExpression(self):
|
||||
node = self.createJSXChildNode()
|
||||
self.collectComments()
|
||||
self.lastMarker.index = self.scanner.index
|
||||
self.lastMarker.line = self.scanner.lineNumber
|
||||
self.lastMarker.column = self.scanner.index - self.scanner.lineStart
|
||||
return self.finalize(node, JSXNode.JSXEmptyExpression())
|
||||
|
||||
def parseJSXExpressionContainer(self):
|
||||
node = self.createJSXNode()
|
||||
self.expectJSX('{')
|
||||
|
||||
if self.matchJSX('}'):
|
||||
expression = self.parseJSXEmptyExpression()
|
||||
self.expectJSX('}')
|
||||
else:
|
||||
self.finishJSX()
|
||||
expression = self.parseAssignmentExpression()
|
||||
self.reenterJSX()
|
||||
|
||||
return self.finalize(node, JSXNode.JSXExpressionContainer(expression))
|
||||
|
||||
def parseJSXChildren(self):
|
||||
children = []
|
||||
|
||||
while not self.scanner.eof():
|
||||
node = self.createJSXChildNode()
|
||||
token = self.nextJSXText()
|
||||
if token.start < token.end:
|
||||
raw = self.getTokenRaw(token)
|
||||
child = self.finalize(node, JSXNode.JSXText(token.value, raw))
|
||||
children.append(child)
|
||||
|
||||
if self.scanner.source[self.scanner.index] == '{':
|
||||
container = self.parseJSXExpressionContainer()
|
||||
children.append(container)
|
||||
else:
|
||||
break
|
||||
|
||||
return children
|
||||
|
||||
def parseComplexJSXElement(self, el):
|
||||
stack = []
|
||||
|
||||
while not self.scanner.eof():
|
||||
el.children.extend(self.parseJSXChildren())
|
||||
node = self.createJSXChildNode()
|
||||
element = self.parseJSXBoundaryElement()
|
||||
if element.type is JSXSyntax.JSXOpeningElement:
|
||||
opening = element
|
||||
if opening.selfClosing:
|
||||
child = self.finalize(node, JSXNode.JSXElement(opening, [], None))
|
||||
el.children.append(child)
|
||||
else:
|
||||
stack.append(el)
|
||||
el = MetaJSXElement(
|
||||
node=node,
|
||||
opening=opening,
|
||||
closing=None,
|
||||
children=[],
|
||||
)
|
||||
|
||||
if element.type is JSXSyntax.JSXClosingElement:
|
||||
el.closing = element
|
||||
open = getQualifiedElementName(el.opening.name)
|
||||
close = getQualifiedElementName(el.closing.name)
|
||||
if open != close:
|
||||
self.tolerateError('Expected corresponding JSX closing tag for %0', open)
|
||||
|
||||
if stack:
|
||||
child = self.finalize(el.node, JSXNode.JSXElement(el.opening, el.children, el.closing))
|
||||
el = stack[-1]
|
||||
el.children.append(child)
|
||||
stack.pop()
|
||||
else:
|
||||
break
|
||||
|
||||
return el
|
||||
|
||||
def parseJSXElement(self):
|
||||
node = self.createJSXNode()
|
||||
|
||||
opening = self.parseJSXOpeningElement()
|
||||
children = []
|
||||
closing = None
|
||||
|
||||
if not opening.selfClosing:
|
||||
el = self.parseComplexJSXElement(MetaJSXElement(
|
||||
node=node,
|
||||
opening=opening,
|
||||
closing=closing,
|
||||
children=children
|
||||
))
|
||||
children = el.children
|
||||
closing = el.closing
|
||||
|
||||
return self.finalize(node, JSXNode.JSXElement(opening, children, closing))
|
||||
|
||||
def parseJSXRoot(self):
|
||||
# Pop the opening '<' added from the lookahead.
|
||||
if self.config.tokens:
|
||||
self.tokens.pop()
|
||||
|
||||
self.startJSX()
|
||||
element = self.parseJSXElement()
|
||||
self.finishJSX()
|
||||
|
||||
return element
|
||||
|
||||
def isStartOfExpression(self):
|
||||
return super(JSXParser, self).isStartOfExpression() or self.match('<')
|
|
@ -0,0 +1,38 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
class JSXSyntax:
|
||||
JSXAttribute = "JSXAttribute"
|
||||
JSXClosingElement = "JSXClosingElement"
|
||||
JSXElement = "JSXElement"
|
||||
JSXEmptyExpression = "JSXEmptyExpression"
|
||||
JSXExpressionContainer = "JSXExpressionContainer"
|
||||
JSXIdentifier = "JSXIdentifier"
|
||||
JSXMemberExpression = "JSXMemberExpression"
|
||||
JSXNamespacedName = "JSXNamespacedName"
|
||||
JSXOpeningElement = "JSXOpeningElement"
|
||||
JSXSpreadAttribute = "JSXSpreadAttribute"
|
||||
JSXText = "JSXText"
|
|
@ -0,0 +1,90 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
# Error messages should be identical to V8.
|
||||
class Messages:
|
||||
ObjectPatternAsRestParameter = "Unexpected token {"
|
||||
BadImportCallArity = "Unexpected token"
|
||||
BadGetterArity = "Getter must not have any formal parameters"
|
||||
BadSetterArity = "Setter must have exactly one formal parameter"
|
||||
BadSetterRestParameter = "Setter function argument must not be a rest parameter"
|
||||
ConstructorIsAsync = "Class constructor may not be an async method"
|
||||
ConstructorSpecialMethod = "Class constructor may not be an accessor"
|
||||
DeclarationMissingInitializer = "Missing initializer in %0 declaration"
|
||||
DefaultRestParameter = "Unexpected token ="
|
||||
DefaultRestProperty = "Unexpected token ="
|
||||
DuplicateBinding = "Duplicate binding %0"
|
||||
DuplicateConstructor = "A class may only have one constructor"
|
||||
DuplicateProtoProperty = "Duplicate __proto__ fields are not allowed in object literals"
|
||||
ForInOfLoopInitializer = "%0 loop variable declaration may not have an initializer"
|
||||
GeneratorInLegacyContext = "Generator declarations are not allowed in legacy contexts"
|
||||
IllegalBreak = "Illegal break statement"
|
||||
IllegalContinue = "Illegal continue statement"
|
||||
IllegalExportDeclaration = "Unexpected token"
|
||||
IllegalImportDeclaration = "Unexpected token"
|
||||
IllegalLanguageModeDirective = "Illegal 'use strict' directive in function with non-simple parameter list"
|
||||
IllegalReturn = "Illegal return statement"
|
||||
InvalidEscapedReservedWord = "Keyword must not contain escaped characters"
|
||||
InvalidHexEscapeSequence = "Invalid hexadecimal escape sequence"
|
||||
InvalidLHSInAssignment = "Invalid left-hand side in assignment"
|
||||
InvalidLHSInForIn = "Invalid left-hand side in for-in"
|
||||
InvalidLHSInForLoop = "Invalid left-hand side in for-loop"
|
||||
InvalidModuleSpecifier = "Unexpected token"
|
||||
InvalidRegExp = "Invalid regular expression"
|
||||
LetInLexicalBinding = "let is disallowed as a lexically bound name"
|
||||
MissingFromClause = "Unexpected token"
|
||||
MultipleDefaultsInSwitch = "More than one default clause in switch statement"
|
||||
NewlineAfterThrow = "Illegal newline after throw"
|
||||
NoAsAfterImportNamespace = "Unexpected token"
|
||||
NoCatchOrFinally = "Missing catch or finally after try"
|
||||
ParameterAfterRestParameter = "Rest parameter must be last formal parameter"
|
||||
PropertyAfterRestProperty = "Unexpected token"
|
||||
Redeclaration = "%0 '%1' has already been declared"
|
||||
StaticPrototype = "Classes may not have static property named prototype"
|
||||
StrictCatchVariable = "Catch variable may not be eval or arguments in strict mode"
|
||||
StrictDelete = "Delete of an unqualified identifier in strict mode."
|
||||
StrictFunction = "In strict mode code, functions can only be declared at top level or inside a block"
|
||||
StrictFunctionName = "Function name may not be eval or arguments in strict mode"
|
||||
StrictLHSAssignment = "Assignment to eval or arguments is not allowed in strict mode"
|
||||
StrictLHSPostfix = "Postfix increment/decrement may not have eval or arguments operand in strict mode"
|
||||
StrictLHSPrefix = "Prefix increment/decrement may not have eval or arguments operand in strict mode"
|
||||
StrictModeWith = "Strict mode code may not include a with statement"
|
||||
StrictOctalLiteral = "Octal literals are not allowed in strict mode."
|
||||
StrictParamDupe = "Strict mode function may not have duplicate parameter names"
|
||||
StrictParamName = "Parameter name eval or arguments is not allowed in strict mode"
|
||||
StrictReservedWord = "Use of future reserved word in strict mode"
|
||||
StrictVarName = "Variable name may not be eval or arguments in strict mode"
|
||||
TemplateOctalLiteral = "Octal literals are not allowed in template strings."
|
||||
UnexpectedEOS = "Unexpected end of input"
|
||||
UnexpectedIdentifier = "Unexpected identifier"
|
||||
UnexpectedNumber = "Unexpected number"
|
||||
UnexpectedReserved = "Unexpected reserved word"
|
||||
UnexpectedString = "Unexpected string"
|
||||
UnexpectedTemplate = "Unexpected quasi %0"
|
||||
UnexpectedToken = "Unexpected token %0"
|
||||
UnexpectedTokenIllegal = "Unexpected token ILLEGAL"
|
||||
UnknownLabel = "Undefined label '%0'"
|
||||
UnterminatedRegExp = "Invalid regular expression: missing /"
|
|
@ -0,0 +1,620 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .objects import Object
|
||||
from .syntax import Syntax
|
||||
from .scanner import RegExp
|
||||
|
||||
|
||||
class Node(Object):
|
||||
def __dir__(self):
|
||||
return list(self.__dict__.keys())
|
||||
|
||||
def __iter__(self):
|
||||
return self.__iter__
|
||||
|
||||
def keys(self):
|
||||
return self.__dict__.keys()
|
||||
|
||||
def items(self):
|
||||
return self.__dict__.items()
|
||||
|
||||
|
||||
class ArrayExpression(Node):
|
||||
def __init__(self, elements):
|
||||
self.type = Syntax.ArrayExpression
|
||||
self.elements = elements
|
||||
|
||||
|
||||
class ArrayPattern(Node):
|
||||
def __init__(self, elements):
|
||||
self.type = Syntax.ArrayPattern
|
||||
self.elements = elements
|
||||
|
||||
|
||||
class ArrowFunctionExpression(Node):
|
||||
def __init__(self, params, body, expression):
|
||||
self.type = Syntax.ArrowFunctionExpression
|
||||
self.generator = False
|
||||
self.isAsync = False
|
||||
self.params = params
|
||||
self.body = body
|
||||
self.expression = expression
|
||||
|
||||
|
||||
class AssignmentExpression(Node):
|
||||
def __init__(self, operator, left, right):
|
||||
self.type = Syntax.AssignmentExpression
|
||||
self.operator = operator
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
|
||||
class AssignmentPattern(Node):
|
||||
def __init__(self, left, right):
|
||||
self.type = Syntax.AssignmentPattern
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
|
||||
class AsyncArrowFunctionExpression(Node):
|
||||
def __init__(self, params, body, expression):
|
||||
self.type = Syntax.ArrowFunctionExpression
|
||||
self.generator = False
|
||||
self.isAsync = True
|
||||
self.params = params
|
||||
self.body = body
|
||||
self.expression = expression
|
||||
|
||||
|
||||
class AsyncFunctionDeclaration(Node):
|
||||
def __init__(self, id, params, body):
|
||||
self.type = Syntax.FunctionDeclaration
|
||||
self.generator = False
|
||||
self.expression = False
|
||||
self.isAsync = True
|
||||
self.id = id
|
||||
self.params = params
|
||||
self.body = body
|
||||
|
||||
|
||||
class AsyncFunctionExpression(Node):
|
||||
def __init__(self, id, params, body):
|
||||
self.type = Syntax.FunctionExpression
|
||||
self.generator = False
|
||||
self.expression = False
|
||||
self.isAsync = True
|
||||
self.id = id
|
||||
self.params = params
|
||||
self.body = body
|
||||
|
||||
|
||||
class AwaitExpression(Node):
|
||||
def __init__(self, argument):
|
||||
self.type = Syntax.AwaitExpression
|
||||
self.argument = argument
|
||||
|
||||
|
||||
class BinaryExpression(Node):
|
||||
def __init__(self, operator, left, right):
|
||||
self.type = Syntax.LogicalExpression if operator in ('||', '&&') else Syntax.BinaryExpression
|
||||
self.operator = operator
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
|
||||
class BlockStatement(Node):
|
||||
def __init__(self, body):
|
||||
self.type = Syntax.BlockStatement
|
||||
self.body = body
|
||||
|
||||
|
||||
class BreakStatement(Node):
|
||||
def __init__(self, label):
|
||||
self.type = Syntax.BreakStatement
|
||||
self.label = label
|
||||
|
||||
|
||||
class CallExpression(Node):
|
||||
def __init__(self, callee, args):
|
||||
self.type = Syntax.CallExpression
|
||||
self.callee = callee
|
||||
self.arguments = args
|
||||
|
||||
|
||||
class CatchClause(Node):
|
||||
def __init__(self, param, body):
|
||||
self.type = Syntax.CatchClause
|
||||
self.param = param
|
||||
self.body = body
|
||||
|
||||
|
||||
class ClassBody(Node):
|
||||
def __init__(self, body):
|
||||
self.type = Syntax.ClassBody
|
||||
self.body = body
|
||||
|
||||
|
||||
class ClassDeclaration(Node):
|
||||
def __init__(self, id, superClass, body):
|
||||
self.type = Syntax.ClassDeclaration
|
||||
self.id = id
|
||||
self.superClass = superClass
|
||||
self.body = body
|
||||
|
||||
|
||||
class ClassExpression(Node):
|
||||
def __init__(self, id, superClass, body):
|
||||
self.type = Syntax.ClassExpression
|
||||
self.id = id
|
||||
self.superClass = superClass
|
||||
self.body = body
|
||||
|
||||
|
||||
class ComputedMemberExpression(Node):
|
||||
def __init__(self, object, property):
|
||||
self.type = Syntax.MemberExpression
|
||||
self.computed = True
|
||||
self.object = object
|
||||
self.property = property
|
||||
|
||||
|
||||
class ConditionalExpression(Node):
|
||||
def __init__(self, test, consequent, alternate):
|
||||
self.type = Syntax.ConditionalExpression
|
||||
self.test = test
|
||||
self.consequent = consequent
|
||||
self.alternate = alternate
|
||||
|
||||
|
||||
class ContinueStatement(Node):
|
||||
def __init__(self, label):
|
||||
self.type = Syntax.ContinueStatement
|
||||
self.label = label
|
||||
|
||||
|
||||
class DebuggerStatement(Node):
|
||||
def __init__(self):
|
||||
self.type = Syntax.DebuggerStatement
|
||||
|
||||
|
||||
class Directive(Node):
|
||||
def __init__(self, expression, directive):
|
||||
self.type = Syntax.ExpressionStatement
|
||||
self.expression = expression
|
||||
self.directive = directive
|
||||
|
||||
|
||||
class DoWhileStatement(Node):
|
||||
def __init__(self, body, test):
|
||||
self.type = Syntax.DoWhileStatement
|
||||
self.body = body
|
||||
self.test = test
|
||||
|
||||
|
||||
class EmptyStatement(Node):
|
||||
def __init__(self):
|
||||
self.type = Syntax.EmptyStatement
|
||||
|
||||
|
||||
class ExportAllDeclaration(Node):
|
||||
def __init__(self, source):
|
||||
self.type = Syntax.ExportAllDeclaration
|
||||
self.source = source
|
||||
|
||||
|
||||
class ExportDefaultDeclaration(Node):
|
||||
def __init__(self, declaration):
|
||||
self.type = Syntax.ExportDefaultDeclaration
|
||||
self.declaration = declaration
|
||||
|
||||
|
||||
class ExportNamedDeclaration(Node):
|
||||
def __init__(self, declaration, specifiers, source):
|
||||
self.type = Syntax.ExportNamedDeclaration
|
||||
self.declaration = declaration
|
||||
self.specifiers = specifiers
|
||||
self.source = source
|
||||
|
||||
|
||||
class ExportSpecifier(Node):
|
||||
def __init__(self, local, exported):
|
||||
self.type = Syntax.ExportSpecifier
|
||||
self.exported = exported
|
||||
self.local = local
|
||||
|
||||
|
||||
class ExportDefaultSpecifier(Node):
|
||||
def __init__(self, local):
|
||||
self.type = Syntax.ExportDefaultSpecifier
|
||||
self.local = local
|
||||
|
||||
|
||||
class ExpressionStatement(Node):
|
||||
def __init__(self, expression):
|
||||
self.type = Syntax.ExpressionStatement
|
||||
self.expression = expression
|
||||
|
||||
|
||||
class ForInStatement(Node):
|
||||
def __init__(self, left, right, body):
|
||||
self.type = Syntax.ForInStatement
|
||||
self.each = False
|
||||
self.left = left
|
||||
self.right = right
|
||||
self.body = body
|
||||
|
||||
|
||||
class ForOfStatement(Node):
|
||||
def __init__(self, left, right, body):
|
||||
self.type = Syntax.ForOfStatement
|
||||
self.left = left
|
||||
self.right = right
|
||||
self.body = body
|
||||
|
||||
|
||||
class ForStatement(Node):
|
||||
def __init__(self, init, test, update, body):
|
||||
self.type = Syntax.ForStatement
|
||||
self.init = init
|
||||
self.test = test
|
||||
self.update = update
|
||||
self.body = body
|
||||
|
||||
|
||||
class FunctionDeclaration(Node):
|
||||
def __init__(self, id, params, body, generator):
|
||||
self.type = Syntax.FunctionDeclaration
|
||||
self.expression = False
|
||||
self.isAsync = False
|
||||
self.id = id
|
||||
self.params = params
|
||||
self.body = body
|
||||
self.generator = generator
|
||||
|
||||
|
||||
class FunctionExpression(Node):
|
||||
def __init__(self, id, params, body, generator):
|
||||
self.type = Syntax.FunctionExpression
|
||||
self.expression = False
|
||||
self.isAsync = False
|
||||
self.id = id
|
||||
self.params = params
|
||||
self.body = body
|
||||
self.generator = generator
|
||||
|
||||
|
||||
class Identifier(Node):
|
||||
def __init__(self, name):
|
||||
self.type = Syntax.Identifier
|
||||
self.name = name
|
||||
|
||||
|
||||
class IfStatement(Node):
|
||||
def __init__(self, test, consequent, alternate):
|
||||
self.type = Syntax.IfStatement
|
||||
self.test = test
|
||||
self.consequent = consequent
|
||||
self.alternate = alternate
|
||||
|
||||
|
||||
class Import(Node):
|
||||
def __init__(self):
|
||||
self.type = Syntax.Import
|
||||
|
||||
|
||||
class ImportDeclaration(Node):
|
||||
def __init__(self, specifiers, source):
|
||||
self.type = Syntax.ImportDeclaration
|
||||
self.specifiers = specifiers
|
||||
self.source = source
|
||||
|
||||
|
||||
class ImportDefaultSpecifier(Node):
|
||||
def __init__(self, local):
|
||||
self.type = Syntax.ImportDefaultSpecifier
|
||||
self.local = local
|
||||
|
||||
|
||||
class ImportNamespaceSpecifier(Node):
|
||||
def __init__(self, local):
|
||||
self.type = Syntax.ImportNamespaceSpecifier
|
||||
self.local = local
|
||||
|
||||
|
||||
class ImportSpecifier(Node):
|
||||
def __init__(self, local, imported):
|
||||
self.type = Syntax.ImportSpecifier
|
||||
self.local = local
|
||||
self.imported = imported
|
||||
|
||||
|
||||
class LabeledStatement(Node):
|
||||
def __init__(self, label, body):
|
||||
self.type = Syntax.LabeledStatement
|
||||
self.label = label
|
||||
self.body = body
|
||||
|
||||
|
||||
class Literal(Node):
|
||||
def __init__(self, value, raw):
|
||||
self.type = Syntax.Literal
|
||||
self.value = value
|
||||
self.raw = raw
|
||||
|
||||
|
||||
class MetaProperty(Node):
|
||||
def __init__(self, meta, property):
|
||||
self.type = Syntax.MetaProperty
|
||||
self.meta = meta
|
||||
self.property = property
|
||||
|
||||
|
||||
class MethodDefinition(Node):
|
||||
def __init__(self, key, computed, value, kind, isStatic):
|
||||
self.type = Syntax.MethodDefinition
|
||||
self.key = key
|
||||
self.computed = computed
|
||||
self.value = value
|
||||
self.kind = kind
|
||||
self.static = isStatic
|
||||
|
||||
|
||||
class FieldDefinition(Node):
|
||||
def __init__(self, key, computed, value, kind, isStatic):
|
||||
self.type = Syntax.FieldDefinition
|
||||
self.key = key
|
||||
self.computed = computed
|
||||
self.value = value
|
||||
self.kind = kind
|
||||
self.static = isStatic
|
||||
|
||||
|
||||
class Module(Node):
|
||||
def __init__(self, body):
|
||||
self.type = Syntax.Program
|
||||
self.sourceType = 'module'
|
||||
self.body = body
|
||||
|
||||
|
||||
class NewExpression(Node):
|
||||
def __init__(self, callee, args):
|
||||
self.type = Syntax.NewExpression
|
||||
self.callee = callee
|
||||
self.arguments = args
|
||||
|
||||
|
||||
class ObjectExpression(Node):
|
||||
def __init__(self, properties):
|
||||
self.type = Syntax.ObjectExpression
|
||||
self.properties = properties
|
||||
|
||||
|
||||
class ObjectPattern(Node):
|
||||
def __init__(self, properties):
|
||||
self.type = Syntax.ObjectPattern
|
||||
self.properties = properties
|
||||
|
||||
|
||||
class Property(Node):
|
||||
def __init__(self, kind, key, computed, value, method, shorthand):
|
||||
self.type = Syntax.Property
|
||||
self.key = key
|
||||
self.computed = computed
|
||||
self.value = value
|
||||
self.kind = kind
|
||||
self.method = method
|
||||
self.shorthand = shorthand
|
||||
|
||||
|
||||
class RegexLiteral(Node):
|
||||
def __init__(self, value, raw, pattern, flags):
|
||||
self.type = Syntax.Literal
|
||||
self.value = value
|
||||
self.raw = raw
|
||||
self.regex = RegExp(
|
||||
pattern=pattern,
|
||||
flags=flags,
|
||||
)
|
||||
|
||||
|
||||
class RestElement(Node):
|
||||
def __init__(self, argument):
|
||||
self.type = Syntax.RestElement
|
||||
self.argument = argument
|
||||
|
||||
|
||||
class ReturnStatement(Node):
|
||||
def __init__(self, argument):
|
||||
self.type = Syntax.ReturnStatement
|
||||
self.argument = argument
|
||||
|
||||
|
||||
class Script(Node):
|
||||
def __init__(self, body):
|
||||
self.type = Syntax.Program
|
||||
self.sourceType = 'script'
|
||||
self.body = body
|
||||
|
||||
|
||||
class SequenceExpression(Node):
|
||||
def __init__(self, expressions):
|
||||
self.type = Syntax.SequenceExpression
|
||||
self.expressions = expressions
|
||||
|
||||
|
||||
class SpreadElement(Node):
|
||||
def __init__(self, argument):
|
||||
self.type = Syntax.SpreadElement
|
||||
self.argument = argument
|
||||
|
||||
|
||||
class StaticMemberExpression(Node):
|
||||
def __init__(self, object, property):
|
||||
self.type = Syntax.MemberExpression
|
||||
self.computed = False
|
||||
self.object = object
|
||||
self.property = property
|
||||
|
||||
|
||||
class Super(Node):
|
||||
def __init__(self):
|
||||
self.type = Syntax.Super
|
||||
|
||||
|
||||
class SwitchCase(Node):
|
||||
def __init__(self, test, consequent):
|
||||
self.type = Syntax.SwitchCase
|
||||
self.test = test
|
||||
self.consequent = consequent
|
||||
|
||||
|
||||
class SwitchStatement(Node):
|
||||
def __init__(self, discriminant, cases):
|
||||
self.type = Syntax.SwitchStatement
|
||||
self.discriminant = discriminant
|
||||
self.cases = cases
|
||||
|
||||
|
||||
class TaggedTemplateExpression(Node):
|
||||
def __init__(self, tag, quasi):
|
||||
self.type = Syntax.TaggedTemplateExpression
|
||||
self.tag = tag
|
||||
self.quasi = quasi
|
||||
|
||||
|
||||
class TemplateElement(Node):
|
||||
class Value(Object):
|
||||
def __init__(self, raw, cooked):
|
||||
self.raw = raw
|
||||
self.cooked = cooked
|
||||
|
||||
def __init__(self, raw, cooked, tail):
|
||||
self.type = Syntax.TemplateElement
|
||||
self.value = TemplateElement.Value(raw, cooked)
|
||||
self.tail = tail
|
||||
|
||||
|
||||
class TemplateLiteral(Node):
|
||||
def __init__(self, quasis, expressions):
|
||||
self.type = Syntax.TemplateLiteral
|
||||
self.quasis = quasis
|
||||
self.expressions = expressions
|
||||
|
||||
|
||||
class ThisExpression(Node):
|
||||
def __init__(self):
|
||||
self.type = Syntax.ThisExpression
|
||||
|
||||
|
||||
class ThrowStatement(Node):
|
||||
def __init__(self, argument):
|
||||
self.type = Syntax.ThrowStatement
|
||||
self.argument = argument
|
||||
|
||||
|
||||
class TryStatement(Node):
|
||||
def __init__(self, block, handler, finalizer):
|
||||
self.type = Syntax.TryStatement
|
||||
self.block = block
|
||||
self.handler = handler
|
||||
self.finalizer = finalizer
|
||||
|
||||
|
||||
class UnaryExpression(Node):
|
||||
def __init__(self, operator, argument):
|
||||
self.type = Syntax.UnaryExpression
|
||||
self.prefix = True
|
||||
self.operator = operator
|
||||
self.argument = argument
|
||||
|
||||
|
||||
class UpdateExpression(Node):
|
||||
def __init__(self, operator, argument, prefix):
|
||||
self.type = Syntax.UpdateExpression
|
||||
self.operator = operator
|
||||
self.argument = argument
|
||||
self.prefix = prefix
|
||||
|
||||
|
||||
class VariableDeclaration(Node):
|
||||
def __init__(self, declarations, kind):
|
||||
self.type = Syntax.VariableDeclaration
|
||||
self.declarations = declarations
|
||||
self.kind = kind
|
||||
|
||||
|
||||
class VariableDeclarator(Node):
|
||||
def __init__(self, id, init):
|
||||
self.type = Syntax.VariableDeclarator
|
||||
self.id = id
|
||||
self.init = init
|
||||
|
||||
|
||||
class WhileStatement(Node):
|
||||
def __init__(self, test, body):
|
||||
self.type = Syntax.WhileStatement
|
||||
self.test = test
|
||||
self.body = body
|
||||
|
||||
|
||||
class WithStatement(Node):
|
||||
def __init__(self, object, body):
|
||||
self.type = Syntax.WithStatement
|
||||
self.object = object
|
||||
self.body = body
|
||||
|
||||
|
||||
class YieldExpression(Node):
|
||||
def __init__(self, argument, delegate):
|
||||
self.type = Syntax.YieldExpression
|
||||
self.argument = argument
|
||||
self.delegate = delegate
|
||||
|
||||
|
||||
class ArrowParameterPlaceHolder(Node):
|
||||
def __init__(self, params):
|
||||
self.type = Syntax.ArrowParameterPlaceHolder
|
||||
self.params = params
|
||||
self.isAsync = False
|
||||
|
||||
|
||||
class AsyncArrowParameterPlaceHolder(Node):
|
||||
def __init__(self, params):
|
||||
self.type = Syntax.ArrowParameterPlaceHolder
|
||||
self.params = params
|
||||
self.isAsync = True
|
||||
|
||||
|
||||
class BlockComment(Node):
|
||||
def __init__(self, value):
|
||||
self.type = Syntax.BlockComment
|
||||
self.value = value
|
||||
|
||||
|
||||
class LineComment(Node):
|
||||
def __init__(self, value):
|
||||
self.type = Syntax.LineComment
|
||||
self.value = value
|
|
@ -0,0 +1,46 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
|
||||
def toDict(value):
|
||||
from .visitor import ToDictVisitor
|
||||
return ToDictVisitor().visit(value)
|
||||
|
||||
|
||||
class Array(list):
|
||||
pass
|
||||
|
||||
|
||||
class Object(object):
|
||||
def toDict(self):
|
||||
from .visitor import ToDictVisitor
|
||||
return ToDictVisitor().visit(self)
|
||||
|
||||
def __repr__(self):
|
||||
from .visitor import ReprVisitor
|
||||
return ReprVisitor().visit(self)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return None
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,100 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
class Syntax:
|
||||
AssignmentExpression = "AssignmentExpression"
|
||||
AssignmentPattern = "AssignmentPattern"
|
||||
ArrayExpression = "ArrayExpression"
|
||||
ArrayPattern = "ArrayPattern"
|
||||
ArrowFunctionExpression = "ArrowFunctionExpression"
|
||||
AwaitExpression = "AwaitExpression"
|
||||
BlockStatement = "BlockStatement"
|
||||
BinaryExpression = "BinaryExpression"
|
||||
BreakStatement = "BreakStatement"
|
||||
CallExpression = "CallExpression"
|
||||
CatchClause = "CatchClause"
|
||||
ClassBody = "ClassBody"
|
||||
ClassDeclaration = "ClassDeclaration"
|
||||
ClassExpression = "ClassExpression"
|
||||
ConditionalExpression = "ConditionalExpression"
|
||||
ContinueStatement = "ContinueStatement"
|
||||
DoWhileStatement = "DoWhileStatement"
|
||||
DebuggerStatement = "DebuggerStatement"
|
||||
EmptyStatement = "EmptyStatement"
|
||||
ExportAllDeclaration = "ExportAllDeclaration"
|
||||
ExportDefaultDeclaration = "ExportDefaultDeclaration"
|
||||
ExportNamedDeclaration = "ExportNamedDeclaration"
|
||||
ExportSpecifier = "ExportSpecifier"
|
||||
ExportDefaultSpecifier = "ExportDefaultSpecifier"
|
||||
ExpressionStatement = "ExpressionStatement"
|
||||
ForStatement = "ForStatement"
|
||||
ForOfStatement = "ForOfStatement"
|
||||
ForInStatement = "ForInStatement"
|
||||
FunctionDeclaration = "FunctionDeclaration"
|
||||
FunctionExpression = "FunctionExpression"
|
||||
Identifier = "Identifier"
|
||||
IfStatement = "IfStatement"
|
||||
Import = "Import"
|
||||
ImportDeclaration = "ImportDeclaration"
|
||||
ImportDefaultSpecifier = "ImportDefaultSpecifier"
|
||||
ImportNamespaceSpecifier = "ImportNamespaceSpecifier"
|
||||
ImportSpecifier = "ImportSpecifier"
|
||||
Literal = "Literal"
|
||||
LabeledStatement = "LabeledStatement"
|
||||
LogicalExpression = "LogicalExpression"
|
||||
MemberExpression = "MemberExpression"
|
||||
MetaProperty = "MetaProperty"
|
||||
MethodDefinition = "MethodDefinition"
|
||||
FieldDefinition = "FieldDefinition"
|
||||
NewExpression = "NewExpression"
|
||||
ObjectExpression = "ObjectExpression"
|
||||
ObjectPattern = "ObjectPattern"
|
||||
Program = "Program"
|
||||
Property = "Property"
|
||||
RestElement = "RestElement"
|
||||
ReturnStatement = "ReturnStatement"
|
||||
SequenceExpression = "SequenceExpression"
|
||||
SpreadElement = "SpreadElement"
|
||||
Super = "Super"
|
||||
SwitchCase = "SwitchCase"
|
||||
SwitchStatement = "SwitchStatement"
|
||||
TaggedTemplateExpression = "TaggedTemplateExpression"
|
||||
TemplateElement = "TemplateElement"
|
||||
TemplateLiteral = "TemplateLiteral"
|
||||
ThisExpression = "ThisExpression"
|
||||
ThrowStatement = "ThrowStatement"
|
||||
TryStatement = "TryStatement"
|
||||
UnaryExpression = "UnaryExpression"
|
||||
UpdateExpression = "UpdateExpression"
|
||||
VariableDeclaration = "VariableDeclaration"
|
||||
VariableDeclarator = "VariableDeclarator"
|
||||
WhileStatement = "WhileStatement"
|
||||
WithStatement = "WithStatement"
|
||||
YieldExpression = "YieldExpression"
|
||||
|
||||
ArrowParameterPlaceHolder = "ArrowParameterPlaceHolder"
|
||||
BlockComment = "BlockComment"
|
||||
LineComment = "LineComment"
|
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
class Token:
|
||||
BooleanLiteral = 1
|
||||
EOF = 2
|
||||
Identifier = 3
|
||||
Keyword = 4
|
||||
NullLiteral = 5
|
||||
NumericLiteral = 6
|
||||
Punctuator = 7
|
||||
StringLiteral = 8
|
||||
RegularExpression = 9
|
||||
Template = 10
|
||||
|
||||
|
||||
TokenName = {}
|
||||
TokenName[Token.BooleanLiteral] = "Boolean"
|
||||
TokenName[Token.EOF] = "<end>"
|
||||
TokenName[Token.Identifier] = "Identifier"
|
||||
TokenName[Token.Keyword] = "Keyword"
|
||||
TokenName[Token.NullLiteral] = "Null"
|
||||
TokenName[Token.NumericLiteral] = "Numeric"
|
||||
TokenName[Token.Punctuator] = "Punctuator"
|
||||
TokenName[Token.StringLiteral] = "String"
|
||||
TokenName[Token.RegularExpression] = "RegularExpression"
|
||||
TokenName[Token.Template] = "Template"
|
|
@ -0,0 +1,193 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES
|
||||
# LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from collections import deque
|
||||
|
||||
from .objects import Object
|
||||
from .error_handler import ErrorHandler
|
||||
from .scanner import Scanner, SourceLocation, Position, RegExp
|
||||
from .token import Token, TokenName
|
||||
|
||||
|
||||
class BufferEntry(Object):
|
||||
def __init__(self, type, value, regex=None, range=None, loc=None):
|
||||
self.type = type
|
||||
self.value = value
|
||||
self.regex = regex
|
||||
self.range = range
|
||||
self.loc = loc
|
||||
|
||||
|
||||
class Reader(object):
|
||||
def __init__(self):
|
||||
self.values = []
|
||||
self.curly = self.paren = -1
|
||||
|
||||
# A function following one of those tokens is an expression.
|
||||
def beforeFunctionExpression(self, t):
|
||||
return t in (
|
||||
'(', '{', '[', 'in', 'typeof', 'instanceof', 'new',
|
||||
'return', 'case', 'delete', 'throw', 'void',
|
||||
# assignment operators
|
||||
'=', '+=', '-=', '*=', '**=', '/=', '%=', '<<=', '>>=', '>>>=',
|
||||
'&=', '|=', '^=', ',',
|
||||
# binary/unary operators
|
||||
'+', '-', '*', '**', '/', '%', '++', '--', '<<', '>>', '>>>', '&',
|
||||
'|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=',
|
||||
'<=', '<', '>', '!=', '!=='
|
||||
)
|
||||
|
||||
# Determine if forward slash (/) is an operator or part of a regular expression
|
||||
# https://github.com/mozilla/sweet.js/wiki/design
|
||||
def isRegexStart(self):
|
||||
if not self.values:
|
||||
return True
|
||||
|
||||
previous = self.values[-1]
|
||||
regex = previous is not None
|
||||
|
||||
if previous in (
|
||||
'this',
|
||||
']',
|
||||
):
|
||||
regex = False
|
||||
elif previous == ')':
|
||||
keyword = self.values[self.paren - 1]
|
||||
regex = keyword in ('if', 'while', 'for', 'with')
|
||||
|
||||
elif previous == '}':
|
||||
# Dividing a function by anything makes little sense,
|
||||
# but we have to check for that.
|
||||
regex = True
|
||||
if len(self.values) >= 3 and self.values[self.curly - 3] == 'function':
|
||||
# Anonymous function, e.g. function(){} /42
|
||||
check = self.values[self.curly - 4]
|
||||
regex = not self.beforeFunctionExpression(check) if check else False
|
||||
elif len(self.values) >= 4 and self.values[self.curly - 4] == 'function':
|
||||
# Named function, e.g. function f(){} /42/
|
||||
check = self.values[self.curly - 5]
|
||||
regex = not self.beforeFunctionExpression(check) if check else True
|
||||
|
||||
return regex
|
||||
|
||||
def append(self, token):
|
||||
if token.type in (Token.Punctuator, Token.Keyword):
|
||||
if token.value == '{':
|
||||
self.curly = len(self.values)
|
||||
elif token.value == '(':
|
||||
self.paren = len(self.values)
|
||||
self.values.append(token.value)
|
||||
else:
|
||||
self.values.append(None)
|
||||
|
||||
|
||||
class Config(Object):
|
||||
def __init__(self, tolerant=None, comment=None, range=None, loc=None, **options):
|
||||
self.tolerant = tolerant
|
||||
self.comment = comment
|
||||
self.range = range
|
||||
self.loc = loc
|
||||
for k, v in options.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
|
||||
class Tokenizer(object):
|
||||
def __init__(self, code, options):
|
||||
self.config = Config(**options)
|
||||
|
||||
self.errorHandler = ErrorHandler()
|
||||
self.errorHandler.tolerant = self.config.tolerant
|
||||
self.scanner = Scanner(code, self.errorHandler)
|
||||
self.scanner.trackComment = self.config.comment
|
||||
|
||||
self.trackRange = self.config.range
|
||||
self.trackLoc = self.config.loc
|
||||
self.buffer = deque()
|
||||
self.reader = Reader()
|
||||
|
||||
def errors(self):
|
||||
return self.errorHandler.errors
|
||||
|
||||
def getNextToken(self):
|
||||
if not self.buffer:
|
||||
|
||||
comments = self.scanner.scanComments()
|
||||
if self.scanner.trackComment:
|
||||
for e in comments:
|
||||
value = self.scanner.source[e.slice[0]:e.slice[1]]
|
||||
comment = BufferEntry(
|
||||
type='BlockComment' if e.multiLine else 'LineComment',
|
||||
value=value
|
||||
)
|
||||
if self.trackRange:
|
||||
comment.range = e.range
|
||||
if self.trackLoc:
|
||||
comment.loc = e.loc
|
||||
self.buffer.append(comment)
|
||||
|
||||
if not self.scanner.eof():
|
||||
if self.trackLoc:
|
||||
loc = SourceLocation(
|
||||
start=Position(
|
||||
line=self.scanner.lineNumber,
|
||||
column=self.scanner.index - self.scanner.lineStart
|
||||
),
|
||||
end=Position(),
|
||||
)
|
||||
|
||||
maybeRegex = self.scanner.source[self.scanner.index] == '/' and self.reader.isRegexStart()
|
||||
if maybeRegex:
|
||||
state = self.scanner.saveState()
|
||||
try:
|
||||
token = self.scanner.scanRegExp()
|
||||
except Exception:
|
||||
self.scanner.restoreState(state)
|
||||
token = self.scanner.lex()
|
||||
else:
|
||||
token = self.scanner.lex()
|
||||
|
||||
self.reader.append(token)
|
||||
|
||||
entry = BufferEntry(
|
||||
type=TokenName[token.type],
|
||||
value=self.scanner.source[token.start:token.end]
|
||||
)
|
||||
if self.trackRange:
|
||||
entry.range = [token.start, token.end]
|
||||
if self.trackLoc:
|
||||
loc.end = Position(
|
||||
line=self.scanner.lineNumber,
|
||||
column=self.scanner.index - self.scanner.lineStart
|
||||
)
|
||||
entry.loc = loc
|
||||
if token.type is Token.RegularExpression:
|
||||
entry.regex = RegExp(
|
||||
pattern=token.pattern,
|
||||
flags=token.flags,
|
||||
)
|
||||
|
||||
self.buffer.append(entry)
|
||||
|
||||
return self.buffer.popleft() if self.buffer else None
|
|
@ -0,0 +1,40 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
from .compat import unicode
|
||||
|
||||
|
||||
def format(messageFormat, *args):
|
||||
def formatter(m):
|
||||
formatter.idx += 1
|
||||
assert formatter.idx < len(args), 'Message reference must be in range'
|
||||
return unicode(args[formatter.idx])
|
||||
formatter.idx = -1
|
||||
return format.re.sub(formatter, messageFormat)
|
||||
|
||||
|
||||
format.re = re.compile(r'%(\d)')
|
|
@ -0,0 +1,288 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
import types
|
||||
from collections import deque
|
||||
|
||||
from .objects import Object
|
||||
from .compat import PY3, unicode
|
||||
|
||||
|
||||
class VisitRecursionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Visited(object):
|
||||
def __init__(self, result):
|
||||
if isinstance(result, Visited):
|
||||
result = result.result
|
||||
self.result = result
|
||||
|
||||
|
||||
class Visitor(object):
|
||||
"""
|
||||
An Object visitor base class that walks the abstract syntax tree and calls a
|
||||
visitor function for every Object found. This function may return a value
|
||||
which is forwarded by the `visit` method.
|
||||
|
||||
This class is meant to be subclassed, with the subclass adding visitor
|
||||
methods.
|
||||
|
||||
Per default the visitor functions for the nodes are ``'visit_'`` +
|
||||
class name of the Object. So a `Module` Object visit function would
|
||||
be `visit_Module`. This behavior can be changed by overriding
|
||||
the `visit` method. If no visitor function exists for a Object
|
||||
(return value `None`) the `generic_visit` visitor is used instead.
|
||||
"""
|
||||
|
||||
def __call__(self, obj, metadata):
|
||||
return self.transform(obj, metadata)
|
||||
|
||||
def transform(self, obj, metadata):
|
||||
"""Transform an Object."""
|
||||
if isinstance(obj, Object):
|
||||
method = 'transform_' + obj.__class__.__name__
|
||||
transformer = getattr(self, method, self.transform_Object)
|
||||
new_obj = transformer(obj, metadata)
|
||||
if new_obj is not None and obj is not new_obj:
|
||||
obj = new_obj
|
||||
return obj
|
||||
|
||||
def transform_Object(self, obj, metadata):
|
||||
"""Called if no explicit transform function exists for an Object."""
|
||||
return obj
|
||||
|
||||
def generic_visit(self, obj):
|
||||
return self.visit(self.visit_Object(obj))
|
||||
|
||||
def visit(self, obj):
|
||||
"""Visit a Object."""
|
||||
if not hasattr(self, 'visitors'):
|
||||
self._visit_context = {}
|
||||
self._visit_count = 0
|
||||
try:
|
||||
self._visit_count += 1
|
||||
stack = deque()
|
||||
stack.append((obj, None))
|
||||
last_result = None
|
||||
while stack:
|
||||
try:
|
||||
last, visited = stack[-1]
|
||||
if isinstance(last, types.GeneratorType):
|
||||
stack.append((last.send(last_result), None))
|
||||
last_result = None
|
||||
elif isinstance(last, Visited):
|
||||
stack.pop()
|
||||
last_result = last.result
|
||||
elif isinstance(last, Object):
|
||||
if last in self._visit_context:
|
||||
if self._visit_context[last] == self.visit_Object:
|
||||
visitor = self.visit_RecursionError
|
||||
else:
|
||||
visitor = self.visit_Object
|
||||
else:
|
||||
method = 'visit_' + last.__class__.__name__
|
||||
visitor = getattr(self, method, self.visit_Object)
|
||||
self._visit_context[last] = visitor
|
||||
stack.pop()
|
||||
stack.append((visitor(last), last))
|
||||
else:
|
||||
method = 'visit_' + last.__class__.__name__
|
||||
visitor = getattr(self, method, self.visit_Generic)
|
||||
stack.pop()
|
||||
stack.append((visitor(last), None))
|
||||
except StopIteration:
|
||||
stack.pop()
|
||||
if visited and visited in self._visit_context:
|
||||
del self._visit_context[visited]
|
||||
return last_result
|
||||
finally:
|
||||
self._visit_count -= 1
|
||||
if self._visit_count <= 0:
|
||||
self._visit_context = {}
|
||||
|
||||
def visit_RecursionError(self, obj):
|
||||
raise VisitRecursionError
|
||||
|
||||
def visit_Object(self, obj):
|
||||
"""Called if no explicit visitor function exists for an Object."""
|
||||
yield obj.__dict__
|
||||
yield Visited(obj)
|
||||
|
||||
def visit_Generic(self, obj):
|
||||
"""Called if no explicit visitor function exists for an object."""
|
||||
yield Visited(obj)
|
||||
|
||||
def visit_list(self, obj):
|
||||
for item in obj:
|
||||
yield item
|
||||
yield Visited(obj)
|
||||
|
||||
visit_Array = visit_list
|
||||
|
||||
def visit_dict(self, obj):
|
||||
for field, value in list(obj.items()):
|
||||
if not field.startswith('_'):
|
||||
yield value
|
||||
yield Visited(obj)
|
||||
|
||||
|
||||
class NodeVisitor(Visitor):
|
||||
pass
|
||||
|
||||
|
||||
class ReprVisitor(Visitor):
|
||||
def visit(self, obj, indent=4, nl="\n", sp="", skip=()):
|
||||
self.level = 0
|
||||
if isinstance(indent, int):
|
||||
indent = " " * indent
|
||||
self.indent = indent
|
||||
self.nl = nl
|
||||
self.sp = sp
|
||||
self.skip = skip
|
||||
return super(ReprVisitor, self).visit(obj)
|
||||
|
||||
def visit_RecursionError(self, obj):
|
||||
yield Visited("...")
|
||||
|
||||
def visit_Object(self, obj):
|
||||
value_repr = yield obj.__dict__
|
||||
yield Visited(value_repr)
|
||||
|
||||
def visit_Generic(self, obj):
|
||||
yield Visited(repr(obj))
|
||||
|
||||
def visit_list(self, obj):
|
||||
indent1 = self.indent * self.level
|
||||
indent2 = indent1 + self.indent
|
||||
self.level += 1
|
||||
try:
|
||||
items = []
|
||||
for item in obj:
|
||||
v = yield item
|
||||
items.append(v)
|
||||
if items:
|
||||
value_repr = "[%s%s%s%s%s%s%s]" % (
|
||||
self.sp,
|
||||
self.nl,
|
||||
indent2,
|
||||
(",%s%s%s" % (self.nl, self.sp, indent2)).join(items),
|
||||
self.nl,
|
||||
indent1,
|
||||
self.sp,
|
||||
)
|
||||
else:
|
||||
value_repr = "[]"
|
||||
finally:
|
||||
self.level -= 1
|
||||
|
||||
yield Visited(value_repr)
|
||||
|
||||
visit_Array = visit_list
|
||||
|
||||
def visit_dict(self, obj):
|
||||
indent1 = self.indent * self.level
|
||||
indent2 = indent1 + self.indent
|
||||
self.level += 1
|
||||
try:
|
||||
items = []
|
||||
for k, item in obj.items():
|
||||
if item is not None and not k.startswith('_') and k not in self.skip:
|
||||
v = yield item
|
||||
items.append("%s: %s" % (k, v))
|
||||
if items:
|
||||
value_repr = "{%s%s%s%s%s%s%s}" % (
|
||||
self.sp,
|
||||
self.nl,
|
||||
indent2,
|
||||
(",%s%s%s" % (self.nl, self.sp, indent2)).join(items),
|
||||
self.nl,
|
||||
indent1,
|
||||
self.sp,
|
||||
)
|
||||
else:
|
||||
value_repr = "{}"
|
||||
finally:
|
||||
self.level -= 1
|
||||
|
||||
yield Visited(value_repr)
|
||||
|
||||
if PY3:
|
||||
def visit_str(self, obj):
|
||||
value_repr = json.dumps(obj)
|
||||
yield Visited(value_repr)
|
||||
else:
|
||||
def visit_unicode(self, obj):
|
||||
value_repr = json.dumps(obj)
|
||||
yield Visited(value_repr)
|
||||
|
||||
def visit_SourceLocation(self, obj):
|
||||
old_indent, self.indent = self.indent, ""
|
||||
old_nl, self.nl = self.nl, ""
|
||||
old_sp, self.sp = self.sp, ""
|
||||
try:
|
||||
yield obj
|
||||
finally:
|
||||
self.indent = old_indent
|
||||
self.nl = old_nl
|
||||
self.sp = old_sp
|
||||
|
||||
|
||||
class ToDictVisitor(Visitor):
|
||||
map = {
|
||||
'isAsync': 'async',
|
||||
'allowAwait': 'await',
|
||||
}
|
||||
|
||||
def visit_RecursionError(self, obj):
|
||||
yield Visited({
|
||||
'error': "Infinite recursion detected...",
|
||||
})
|
||||
|
||||
def visit_Object(self, obj):
|
||||
obj = yield obj.__dict__
|
||||
yield Visited(obj)
|
||||
|
||||
def visit_list(self, obj):
|
||||
items = []
|
||||
for item in obj:
|
||||
v = yield item
|
||||
items.append(v)
|
||||
yield Visited(items)
|
||||
|
||||
visit_Array = visit_list
|
||||
|
||||
def visit_dict(self, obj):
|
||||
items = []
|
||||
for k, item in obj.items():
|
||||
if item is not None and not k.startswith('_'):
|
||||
v = yield item
|
||||
k = unicode(k)
|
||||
items.append((self.map.get(k, k), v))
|
||||
yield Visited(dict(items))
|
||||
|
||||
def visit_SRE_Pattern(self, obj):
|
||||
yield Visited({})
|
|
@ -0,0 +1,281 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Generated by generate-xhtml-entities.js. DO NOT MODIFY!
|
||||
|
||||
XHTMLEntities = {
|
||||
'quot': "\u0022",
|
||||
'amp': "\u0026",
|
||||
'apos': "\u0027",
|
||||
'gt': "\u003E",
|
||||
'nbsp': "\u00A0",
|
||||
'iexcl': "\u00A1",
|
||||
'cent': "\u00A2",
|
||||
'pound': "\u00A3",
|
||||
'curren': "\u00A4",
|
||||
'yen': "\u00A5",
|
||||
'brvbar': "\u00A6",
|
||||
'sect': "\u00A7",
|
||||
'uml': "\u00A8",
|
||||
'copy': "\u00A9",
|
||||
'ordf': "\u00AA",
|
||||
'laquo': "\u00AB",
|
||||
'not': "\u00AC",
|
||||
'shy': "\u00AD",
|
||||
'reg': "\u00AE",
|
||||
'macr': "\u00AF",
|
||||
'deg': "\u00B0",
|
||||
'plusmn': "\u00B1",
|
||||
'sup2': "\u00B2",
|
||||
'sup3': "\u00B3",
|
||||
'acute': "\u00B4",
|
||||
'micro': "\u00B5",
|
||||
'para': "\u00B6",
|
||||
'middot': "\u00B7",
|
||||
'cedil': "\u00B8",
|
||||
'sup1': "\u00B9",
|
||||
'ordm': "\u00BA",
|
||||
'raquo': "\u00BB",
|
||||
'frac14': "\u00BC",
|
||||
'frac12': "\u00BD",
|
||||
'frac34': "\u00BE",
|
||||
'iquest': "\u00BF",
|
||||
'Agrave': "\u00C0",
|
||||
'Aacute': "\u00C1",
|
||||
'Acirc': "\u00C2",
|
||||
'Atilde': "\u00C3",
|
||||
'Auml': "\u00C4",
|
||||
'Aring': "\u00C5",
|
||||
'AElig': "\u00C6",
|
||||
'Ccedil': "\u00C7",
|
||||
'Egrave': "\u00C8",
|
||||
'Eacute': "\u00C9",
|
||||
'Ecirc': "\u00CA",
|
||||
'Euml': "\u00CB",
|
||||
'Igrave': "\u00CC",
|
||||
'Iacute': "\u00CD",
|
||||
'Icirc': "\u00CE",
|
||||
'Iuml': "\u00CF",
|
||||
'ETH': "\u00D0",
|
||||
'Ntilde': "\u00D1",
|
||||
'Ograve': "\u00D2",
|
||||
'Oacute': "\u00D3",
|
||||
'Ocirc': "\u00D4",
|
||||
'Otilde': "\u00D5",
|
||||
'Ouml': "\u00D6",
|
||||
'times': "\u00D7",
|
||||
'Oslash': "\u00D8",
|
||||
'Ugrave': "\u00D9",
|
||||
'Uacute': "\u00DA",
|
||||
'Ucirc': "\u00DB",
|
||||
'Uuml': "\u00DC",
|
||||
'Yacute': "\u00DD",
|
||||
'THORN': "\u00DE",
|
||||
'szlig': "\u00DF",
|
||||
'agrave': "\u00E0",
|
||||
'aacute': "\u00E1",
|
||||
'acirc': "\u00E2",
|
||||
'atilde': "\u00E3",
|
||||
'auml': "\u00E4",
|
||||
'aring': "\u00E5",
|
||||
'aelig': "\u00E6",
|
||||
'ccedil': "\u00E7",
|
||||
'egrave': "\u00E8",
|
||||
'eacute': "\u00E9",
|
||||
'ecirc': "\u00EA",
|
||||
'euml': "\u00EB",
|
||||
'igrave': "\u00EC",
|
||||
'iacute': "\u00ED",
|
||||
'icirc': "\u00EE",
|
||||
'iuml': "\u00EF",
|
||||
'eth': "\u00F0",
|
||||
'ntilde': "\u00F1",
|
||||
'ograve': "\u00F2",
|
||||
'oacute': "\u00F3",
|
||||
'ocirc': "\u00F4",
|
||||
'otilde': "\u00F5",
|
||||
'ouml': "\u00F6",
|
||||
'divide': "\u00F7",
|
||||
'oslash': "\u00F8",
|
||||
'ugrave': "\u00F9",
|
||||
'uacute': "\u00FA",
|
||||
'ucirc': "\u00FB",
|
||||
'uuml': "\u00FC",
|
||||
'yacute': "\u00FD",
|
||||
'thorn': "\u00FE",
|
||||
'yuml': "\u00FF",
|
||||
'OElig': "\u0152",
|
||||
'oelig': "\u0153",
|
||||
'Scaron': "\u0160",
|
||||
'scaron': "\u0161",
|
||||
'Yuml': "\u0178",
|
||||
'fnof': "\u0192",
|
||||
'circ': "\u02C6",
|
||||
'tilde': "\u02DC",
|
||||
'Alpha': "\u0391",
|
||||
'Beta': "\u0392",
|
||||
'Gamma': "\u0393",
|
||||
'Delta': "\u0394",
|
||||
'Epsilon': "\u0395",
|
||||
'Zeta': "\u0396",
|
||||
'Eta': "\u0397",
|
||||
'Theta': "\u0398",
|
||||
'Iota': "\u0399",
|
||||
'Kappa': "\u039A",
|
||||
'Lambda': "\u039B",
|
||||
'Mu': "\u039C",
|
||||
'Nu': "\u039D",
|
||||
'Xi': "\u039E",
|
||||
'Omicron': "\u039F",
|
||||
'Pi': "\u03A0",
|
||||
'Rho': "\u03A1",
|
||||
'Sigma': "\u03A3",
|
||||
'Tau': "\u03A4",
|
||||
'Upsilon': "\u03A5",
|
||||
'Phi': "\u03A6",
|
||||
'Chi': "\u03A7",
|
||||
'Psi': "\u03A8",
|
||||
'Omega': "\u03A9",
|
||||
'alpha': "\u03B1",
|
||||
'beta': "\u03B2",
|
||||
'gamma': "\u03B3",
|
||||
'delta': "\u03B4",
|
||||
'epsilon': "\u03B5",
|
||||
'zeta': "\u03B6",
|
||||
'eta': "\u03B7",
|
||||
'theta': "\u03B8",
|
||||
'iota': "\u03B9",
|
||||
'kappa': "\u03BA",
|
||||
'lambda': "\u03BB",
|
||||
'mu': "\u03BC",
|
||||
'nu': "\u03BD",
|
||||
'xi': "\u03BE",
|
||||
'omicron': "\u03BF",
|
||||
'pi': "\u03C0",
|
||||
'rho': "\u03C1",
|
||||
'sigmaf': "\u03C2",
|
||||
'sigma': "\u03C3",
|
||||
'tau': "\u03C4",
|
||||
'upsilon': "\u03C5",
|
||||
'phi': "\u03C6",
|
||||
'chi': "\u03C7",
|
||||
'psi': "\u03C8",
|
||||
'omega': "\u03C9",
|
||||
'thetasym': "\u03D1",
|
||||
'upsih': "\u03D2",
|
||||
'piv': "\u03D6",
|
||||
'ensp': "\u2002",
|
||||
'emsp': "\u2003",
|
||||
'thinsp': "\u2009",
|
||||
'zwnj': "\u200C",
|
||||
'zwj': "\u200D",
|
||||
'lrm': "\u200E",
|
||||
'rlm': "\u200F",
|
||||
'ndash': "\u2013",
|
||||
'mdash': "\u2014",
|
||||
'lsquo': "\u2018",
|
||||
'rsquo': "\u2019",
|
||||
'sbquo': "\u201A",
|
||||
'ldquo': "\u201C",
|
||||
'rdquo': "\u201D",
|
||||
'bdquo': "\u201E",
|
||||
'dagger': "\u2020",
|
||||
'Dagger': "\u2021",
|
||||
'bull': "\u2022",
|
||||
'hellip': "\u2026",
|
||||
'permil': "\u2030",
|
||||
'prime': "\u2032",
|
||||
'Prime': "\u2033",
|
||||
'lsaquo': "\u2039",
|
||||
'rsaquo': "\u203A",
|
||||
'oline': "\u203E",
|
||||
'frasl': "\u2044",
|
||||
'euro': "\u20AC",
|
||||
'image': "\u2111",
|
||||
'weierp': "\u2118",
|
||||
'real': "\u211C",
|
||||
'trade': "\u2122",
|
||||
'alefsym': "\u2135",
|
||||
'larr': "\u2190",
|
||||
'uarr': "\u2191",
|
||||
'rarr': "\u2192",
|
||||
'darr': "\u2193",
|
||||
'harr': "\u2194",
|
||||
'crarr': "\u21B5",
|
||||
'lArr': "\u21D0",
|
||||
'uArr': "\u21D1",
|
||||
'rArr': "\u21D2",
|
||||
'dArr': "\u21D3",
|
||||
'hArr': "\u21D4",
|
||||
'forall': "\u2200",
|
||||
'part': "\u2202",
|
||||
'exist': "\u2203",
|
||||
'empty': "\u2205",
|
||||
'nabla': "\u2207",
|
||||
'isin': "\u2208",
|
||||
'notin': "\u2209",
|
||||
'ni': "\u220B",
|
||||
'prod': "\u220F",
|
||||
'sum': "\u2211",
|
||||
'minus': "\u2212",
|
||||
'lowast': "\u2217",
|
||||
'radic': "\u221A",
|
||||
'prop': "\u221D",
|
||||
'infin': "\u221E",
|
||||
'ang': "\u2220",
|
||||
'and': "\u2227",
|
||||
'or': "\u2228",
|
||||
'cap': "\u2229",
|
||||
'cup': "\u222A",
|
||||
'int': "\u222B",
|
||||
'there4': "\u2234",
|
||||
'sim': "\u223C",
|
||||
'cong': "\u2245",
|
||||
'asymp': "\u2248",
|
||||
'ne': "\u2260",
|
||||
'equiv': "\u2261",
|
||||
'le': "\u2264",
|
||||
'ge': "\u2265",
|
||||
'sub': "\u2282",
|
||||
'sup': "\u2283",
|
||||
'nsub': "\u2284",
|
||||
'sube': "\u2286",
|
||||
'supe': "\u2287",
|
||||
'oplus': "\u2295",
|
||||
'otimes': "\u2297",
|
||||
'perp': "\u22A5",
|
||||
'sdot': "\u22C5",
|
||||
'lceil': "\u2308",
|
||||
'rceil': "\u2309",
|
||||
'lfloor': "\u230A",
|
||||
'rfloor': "\u230B",
|
||||
'loz': "\u25CA",
|
||||
'spades': "\u2660",
|
||||
'clubs': "\u2663",
|
||||
'hearts': "\u2665",
|
||||
'diams': "\u2666",
|
||||
'lang': "\u27E8",
|
||||
'rang': "\u27E9",
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
import os
|
||||
|
||||
from esprima import version
|
||||
|
||||
|
||||
def read(fname):
|
||||
try:
|
||||
with open(os.path.join(os.path.dirname(__file__), fname), "r") as fp:
|
||||
return fp.read().strip()
|
||||
except IOError:
|
||||
return ''
|
||||
|
||||
|
||||
setup(
|
||||
name="esprima",
|
||||
version=version,
|
||||
author="German M. Bravo (Kronuz)",
|
||||
author_email="german.mb@gmail.com",
|
||||
url="https://github.com/Kronuz/esprima-python",
|
||||
license="BSD License",
|
||||
keywords="esprima ecmascript javascript parser ast",
|
||||
description="ECMAScript parsing infrastructure for multipurpose analysis in Python",
|
||||
long_description=read("README.rst"),
|
||||
packages=["esprima"],
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Operating System :: OS Independent",
|
||||
"Topic :: Software Development :: Code Generators",
|
||||
"Topic :: Software Development :: Compilers",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: Text Processing :: General",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'esprima = esprima.__main__:main',
|
||||
]
|
||||
},
|
||||
)
|
|
@ -23,6 +23,7 @@ compare-locales==7.2.5
|
|||
cookies==2.2.1
|
||||
distro==1.4.0
|
||||
ecdsa==0.15
|
||||
esprima==4.0.1
|
||||
jsmin==2.1.0
|
||||
json-e==2.7.0
|
||||
mozilla-version==0.3.0
|
||||
|
|
|
@ -38,6 +38,9 @@ ecdsa==0.15 \
|
|||
--hash=sha256:867ec9cf6df0b03addc8ef66b56359643cb5d0c1dc329df76ba7ecfe256c8061 \
|
||||
--hash=sha256:8f12ac317f8a1318efa75757ef0a651abe12e51fc1af8838fb91079445227277 \
|
||||
# via -r requirements-mach-vendor-python.in
|
||||
esprima==4.0.1 \
|
||||
--hash=sha256:08db1a876d3c2910db9cfaeb83108193af5411fc3a3a66ebefacd390d21323ee \
|
||||
# via -r requirements-mach-vendor-python.in
|
||||
fluent.syntax==0.15.1 \
|
||||
--hash=sha256:3d3c95b9de82df498172d9447576e787e809b753c6f7f5acf64a9ef8d7782c81 \
|
||||
--hash=sha256:e95bb3abfe7cf51b2c6d1e92d57335f07c737ba57bbbba18e57618bd15f78d4b \
|
||||
|
|
Загрузка…
Ссылка в новой задаче