mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Replace Python parser with ruff parser (#5494)
* stage1 * compiler pass build * introduce rustpython-compiler-source * stage2 * fixup * pass compile * Fix hello world compiler test * Fix code generation for if-elif-else statement * Fix code generation for lambda expression * Fix code generation for integers * Fix code generation for fstrings * Fix code generation for if statement * Fix code generation for if statement * Fix code generation for if statement * Fix code generation for fstring * Fix code generation for class definition * Replace feature flags * Initialize frozen core modules * Allow __future__ import after module doc comment * Disable ast module * Commit remaining fixes for compile errors in examples * Fix some warnings * Update ast stdlib module * Update ast stdlib module * Update ast stdlib module * Update ast stdlib module * Update ast stdlib module * Split ast stdlib module into files * Fix codegen for positional arguments with defaults * Update ast stdlib module * Update ast stdlib module * Extract string and constant handling from expression.rs * Always add required fields to AST nodes * Compile doc strings correctly again * Enable "ast" Cargo feature by default * Refactor compilation of big integer literal * Update ast stdlib module * Update ast stdlib module * Update ast stdlib module * Reset barebones example * Fix some left-over warnings * Undo accidential change * Adapt shell to ruff parser * Pin parser to v0.4.10 * fix clippy * Add TODO about interactive mode * Fix compilation of complex number expression * Remove moved code * Update test case to ruff v0.4.10 * Apply suggestion Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com> * Apply suggestion Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com> * Fix compilation of fstring expression * Fix compilation of fstring expression * Fix wasm compile errors * Attach correct source locations to ast objects * Fix some more wasm compile errors * Consider compile mode and enable AST stdlib module again * Fix incorrect AST source location end column * Fix compile error if "compiler" feature is not enabled * Fix regrtests * Fix some test_ast tests * Add source range to type ignore * Fix incompatibility with Rust 2024 edition * Fix todos by implementing missing ast conversions and deleting unused code * Appease clippy * Fix remaining ast tests * Fix remaining ast tests * Mark/fix remaining tests * Fix more * Hacky windows fix --------- Co-authored-by: Kangzhi Shi <shikangzhi@gmail.com> Co-authored-by: Jeong YunWon <jeong@youknowone.org> Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com> Co-authored-by: Noa <coolreader18@gmail.com>
This commit is contained in:
70 files changed
+9990
-6867
No files matched your search
Generated
+125
-91
@@ -1007,6 +1007,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
@@ -1051,12 +1060,6 @@ dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop-util"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553"
|
||||
|
||||
[[package]]
|
||||
name = "lambert_w"
|
||||
version = "1.0.17"
|
||||
@@ -1958,6 +1961,90 @@ dependencies = [
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_ast"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=v0.4.10#b54922fd7394c36cdc390fd21aaee99206ebc361"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bitflags 2.8.0",
|
||||
"is-macro",
|
||||
"itertools 0.13.0",
|
||||
"once_cell",
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_codegen"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=v0.4.10#b54922fd7394c36cdc390fd21aaee99206ebc361"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_literal",
|
||||
"ruff_python_parser",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_literal"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=v0.4.10#b54922fd7394c36cdc390fd21aaee99206ebc361"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"itertools 0.13.0",
|
||||
"ruff_python_ast",
|
||||
"unic-ucd-category",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_parser"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=v0.4.10#b54922fd7394c36cdc390fd21aaee99206ebc361"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bstr",
|
||||
"memchr",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_trivia",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 1.1.0",
|
||||
"static_assertions",
|
||||
"unicode-ident",
|
||||
"unicode-normalization",
|
||||
"unicode_names2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_trivia"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=v0.4.10#b54922fd7394c36cdc390fd21aaee99206ebc361"
|
||||
dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_source_file"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=v0.4.10#b54922fd7394c36cdc390fd21aaee99206ebc361"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"ruff_text_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_text_size"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=v0.4.10#b54922fd7394c36cdc390fd21aaee99206ebc361"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@@ -2006,26 +2093,14 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"pyo3",
|
||||
"ruff_python_parser",
|
||||
"rustpython-compiler",
|
||||
"rustpython-parser",
|
||||
"rustpython-pylib",
|
||||
"rustpython-stdlib",
|
||||
"rustpython-vm",
|
||||
"rustyline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/RustPython/Parser.git?rev=d2f137b372ec08ce4a243564a80f8f9153c45a23#d2f137b372ec08ce4a243564a80f8f9153c45a23"
|
||||
dependencies = [
|
||||
"is-macro",
|
||||
"malachite-bigint",
|
||||
"rustpython-literal",
|
||||
"rustpython-parser-core",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-codegen"
|
||||
version = "0.4.0"
|
||||
@@ -2036,13 +2111,18 @@ dependencies = [
|
||||
"insta",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"malachite-bigint",
|
||||
"num-complex",
|
||||
"num-traits",
|
||||
"rustpython-ast",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_codegen",
|
||||
"ruff_python_parser",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustpython-common",
|
||||
"rustpython-compiler-core",
|
||||
"rustpython-parser",
|
||||
"rustpython-parser-core",
|
||||
"rustpython-compiler-source",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2075,9 +2155,14 @@ dependencies = [
|
||||
name = "rustpython-compiler"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"ruff_python_ast",
|
||||
"ruff_python_parser",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustpython-codegen",
|
||||
"rustpython-compiler-core",
|
||||
"rustpython-parser",
|
||||
"rustpython-compiler-source",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2089,10 +2174,20 @@ dependencies = [
|
||||
"lz4_flex",
|
||||
"malachite-bigint",
|
||||
"num-complex",
|
||||
"rustpython-parser-core",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_parser",
|
||||
"ruff_source_file",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-compiler-source"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-derive"
|
||||
version = "0.4.0"
|
||||
@@ -2113,7 +2208,6 @@ dependencies = [
|
||||
"quote",
|
||||
"rustpython-compiler-core",
|
||||
"rustpython-doc",
|
||||
"rustpython-parser-core",
|
||||
"syn 2.0.98",
|
||||
"syn-ext",
|
||||
"textwrap 0.16.1",
|
||||
@@ -2166,48 +2260,6 @@ dependencies = [
|
||||
"unic-ucd-category",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/RustPython/Parser.git?rev=d2f137b372ec08ce4a243564a80f8f9153c45a23#d2f137b372ec08ce4a243564a80f8f9153c45a23"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"is-macro",
|
||||
"itertools 0.11.0",
|
||||
"lalrpop-util",
|
||||
"log",
|
||||
"malachite-bigint",
|
||||
"num-traits",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"rustc-hash 1.1.0",
|
||||
"rustpython-ast",
|
||||
"rustpython-parser-core",
|
||||
"tiny-keccak",
|
||||
"unic-emoji-char",
|
||||
"unic-ucd-ident",
|
||||
"unicode_names2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-parser-core"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/RustPython/Parser.git?rev=d2f137b372ec08ce4a243564a80f8f9153c45a23#d2f137b372ec08ce4a243564a80f8f9153c45a23"
|
||||
dependencies = [
|
||||
"is-macro",
|
||||
"memchr",
|
||||
"rustpython-parser-vendored",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-parser-vendored"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/RustPython/Parser.git?rev=d2f137b372ec08ce4a243564a80f8f9153c45a23#d2f137b372ec08ce4a243564a80f8f9153c45a23"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-pylib"
|
||||
version = "0.4.0"
|
||||
@@ -2344,19 +2396,21 @@ dependencies = [
|
||||
"paste",
|
||||
"rand 0.9.0",
|
||||
"result-like",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_parser",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc_version",
|
||||
"rustix",
|
||||
"rustpython-ast",
|
||||
"rustpython-codegen",
|
||||
"rustpython-common",
|
||||
"rustpython-compiler",
|
||||
"rustpython-compiler-core",
|
||||
"rustpython-compiler-source",
|
||||
"rustpython-derive",
|
||||
"rustpython-format",
|
||||
"rustpython-jit",
|
||||
"rustpython-literal",
|
||||
"rustpython-parser",
|
||||
"rustpython-parser-core",
|
||||
"rustpython-sre_engine",
|
||||
"rustyline",
|
||||
"schannel",
|
||||
@@ -2388,8 +2442,8 @@ dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"getrandom 0.2.15",
|
||||
"js-sys",
|
||||
"ruff_python_parser",
|
||||
"rustpython-common",
|
||||
"rustpython-parser",
|
||||
"rustpython-pylib",
|
||||
"rustpython-stdlib",
|
||||
"rustpython-vm",
|
||||
@@ -2793,15 +2847,6 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "639ce8ef6d2ba56be0383a94dd13b92138d58de44c62618303bb798fa92bdc00"
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
@@ -2879,17 +2924,6 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
|
||||
|
||||
[[package]]
|
||||
name = "unic-emoji-char"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d"
|
||||
dependencies = [
|
||||
"unic-char-property",
|
||||
"unic-char-range",
|
||||
"unic-ucd-version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-normal"
|
||||
version = "0.9.0"
|
||||
|
||||
+11
-5
@@ -28,7 +28,7 @@ rustpython-compiler = { workspace = true }
|
||||
rustpython-pylib = { workspace = true, optional = true }
|
||||
rustpython-stdlib = { workspace = true, optional = true, features = ["compiler"] }
|
||||
rustpython-vm = { workspace = true, features = ["compiler"] }
|
||||
rustpython-parser = { workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
|
||||
cfg-if = { workspace = true }
|
||||
log = { workspace = true }
|
||||
@@ -94,7 +94,7 @@ x86_64-pc-windows-msvc = { triplet = "x64-windows-static-md", dev-dependencies =
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"compiler", "compiler/core", "compiler/codegen",
|
||||
"compiler", "compiler/core", "compiler/codegen", "compiler/source",
|
||||
".", "common", "derive", "jit", "vm", "vm/sre_engine", "pylib", "stdlib", "derive-impl",
|
||||
"wasm/lib",
|
||||
]
|
||||
@@ -108,6 +108,7 @@ repository = "https://github.com/RustPython/RustPython"
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
rustpython-compiler-source = { path = "compiler/source" }
|
||||
rustpython-compiler-core = { path = "compiler/core", version = "0.4.0" }
|
||||
rustpython-compiler = { path = "compiler", version = "0.4.0" }
|
||||
rustpython-codegen = { path = "compiler/codegen", version = "0.4.0" }
|
||||
@@ -121,15 +122,20 @@ rustpython-stdlib = { path = "stdlib", default-features = false, version = "0.4.
|
||||
rustpython-sre_engine = { path = "vm/sre_engine", version = "0.4.0" }
|
||||
rustpython-doc = { git = "https://github.com/RustPython/__doc__", tag = "0.3.0", version = "0.3.0" }
|
||||
|
||||
ruff_python_parser = { git = "https://github.com/astral-sh/ruff.git", tag = "v0.4.10" }
|
||||
ruff_python_ast = { git = "https://github.com/astral-sh/ruff.git", tag = "v0.4.10" }
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/ruff.git", tag = "v0.4.10" }
|
||||
ruff_source_file = { git = "https://github.com/astral-sh/ruff.git", tag = "v0.4.10" }
|
||||
ruff_python_codegen = { git = "https://github.com/astral-sh/ruff.git", tag = "v0.4.10" }
|
||||
# rustpython-literal = { version = "0.4.0" }
|
||||
# rustpython-parser-core = { version = "0.4.0" }
|
||||
# rustpython-parser = { version = "0.4.0" }
|
||||
# rustpython-ast = { version = "0.4.0" }
|
||||
# rustpython-format= { version = "0.4.0" }
|
||||
rustpython-literal = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
|
||||
rustpython-parser-core = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
|
||||
rustpython-parser = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
|
||||
rustpython-ast = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
|
||||
# rustpython-parser-core = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
|
||||
# rustpython-parser = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
|
||||
# rustpython-ast = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
|
||||
rustpython-format = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
|
||||
# rustpython-literal = { path = "../RustPython-parser/literal" }
|
||||
# rustpython-parser-core = { path = "../RustPython-parser/core" }
|
||||
|
||||
Vendored
+8
-6
@@ -388,6 +388,8 @@ class AST_Tests(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
compile(tree, '<string>', 'exec')
|
||||
|
||||
# XXX RUSTPYTHON: we always require that end ranges be present
|
||||
@unittest.expectedFailure
|
||||
def test_compilation_of_ast_nodes_with_default_end_position_values(self):
|
||||
tree = ast.Module(body=[
|
||||
ast.Import(names=[ast.alias(name='builtins', lineno=1, col_offset=0)], lineno=1, col_offset=0),
|
||||
@@ -1531,6 +1533,8 @@ Module(
|
||||
malformed = ast.Dict(keys=[ast.Constant(1)], values=[ast.Constant(2), ast.Constant(3)])
|
||||
self.assertRaises(ValueError, ast.literal_eval, malformed)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_literal_eval_trailing_ws(self):
|
||||
self.assertEqual(ast.literal_eval(" -1"), -1)
|
||||
self.assertEqual(ast.literal_eval("\t\t-1"), -1)
|
||||
@@ -1549,6 +1553,8 @@ Module(
|
||||
with self.assertRaisesRegex(ValueError, msg):
|
||||
ast.literal_eval(node)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_literal_eval_syntax_errors(self):
|
||||
with self.assertRaisesRegex(SyntaxError, "unexpected indent"):
|
||||
ast.literal_eval(r'''
|
||||
@@ -1569,6 +1575,8 @@ Module(
|
||||
compile(mod, 'test', 'exec')
|
||||
self.assertIn("invalid integer value: None", str(cm.exception))
|
||||
|
||||
# XXX RUSTPYTHON: we always require that end ranges be present
|
||||
@unittest.expectedFailure
|
||||
def test_level_as_none(self):
|
||||
body = [ast.ImportFrom(module='time',
|
||||
names=[ast.alias(name='sleep',
|
||||
@@ -2034,8 +2042,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
call = ast.Call(func, args, bad_keywords)
|
||||
self.expr(call, "must have Load context")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_num(self):
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
@@ -2733,8 +2739,6 @@ class EndPositionTests(unittest.TestCase):
|
||||
binop = self._parse_value(s_orig)
|
||||
self.assertEqual(ast.get_source_segment(s_orig, binop.left), s_tuple)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_source_segment_padded(self):
|
||||
s_orig = dedent('''
|
||||
class C:
|
||||
@@ -2756,8 +2760,6 @@ class EndPositionTests(unittest.TestCase):
|
||||
self._check_content(s, y, 'y = 1')
|
||||
self._check_content(s, z, 'z = 1')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_source_segment_tabs(self):
|
||||
s = dedent('''
|
||||
class C:
|
||||
|
||||
Vendored
+2
@@ -323,6 +323,8 @@ class CodeopTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(SyntaxError, message):
|
||||
compile_command(code, symbol='exec')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_syntax_errors(self):
|
||||
self.assertSyntaxErrorMatches(
|
||||
dedent("""\
|
||||
|
||||
Vendored
+10
-2
@@ -7,9 +7,9 @@ from test.support import script_helper
|
||||
from test.support import warnings_helper
|
||||
import unittest
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
class EOFTestCase(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_EOF_single_quote(self):
|
||||
expect = "unterminated string literal (detected at line 1) (<string>, line 1)"
|
||||
for quote in ("'", "\""):
|
||||
@@ -22,6 +22,8 @@ class EOFTestCase(unittest.TestCase):
|
||||
else:
|
||||
raise support.TestFailed
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_EOFS(self):
|
||||
expect = ("unterminated triple-quoted string literal (detected at line 1) (<string>, line 1)")
|
||||
try:
|
||||
@@ -32,6 +34,8 @@ class EOFTestCase(unittest.TestCase):
|
||||
else:
|
||||
raise support.TestFailed
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_EOFS_with_file(self):
|
||||
expect = ("(<string>, line 1)")
|
||||
with os_helper.temp_dir() as temp_dir:
|
||||
@@ -39,6 +43,8 @@ class EOFTestCase(unittest.TestCase):
|
||||
rc, out, err = script_helper.assert_python_failure(file_name)
|
||||
self.assertIn(b'unterminated triple-quoted string literal (detected at line 3)', err)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@warnings_helper.ignore_warnings(category=SyntaxWarning)
|
||||
def test_eof_with_line_continuation(self):
|
||||
expect = "unexpected EOF while parsing (<string>, line 1)"
|
||||
@@ -49,6 +55,8 @@ class EOFTestCase(unittest.TestCase):
|
||||
else:
|
||||
raise support.TestFailed
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_line_continuation_EOF(self):
|
||||
"""A continuation at the end of input must be an error; bpo2180."""
|
||||
expect = 'unexpected EOF while parsing (<string>, line 1)'
|
||||
|
||||
Vendored
+2
@@ -50,6 +50,8 @@ class ExceptionTests(unittest.TestCase):
|
||||
self.assertEqual(buf1, buf2)
|
||||
self.assertEqual(exc.__name__, excname)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def testRaising(self):
|
||||
self.raise_catch(AttributeError, "AttributeError")
|
||||
self.assertRaises(AttributeError, getattr, sys, "undefined_attribute")
|
||||
|
||||
Vendored
+10
-4
@@ -305,8 +305,6 @@ f'{a * x()} {a * x()} {a * x()}'
|
||||
self.assertEqual(name.col_offset, 22)
|
||||
self.assertEqual(name.end_col_offset, 25)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_ast_line_numbers_multiline_fstring(self):
|
||||
# See bpo-30465 for details.
|
||||
expr = """
|
||||
@@ -509,8 +507,6 @@ x = (
|
||||
self.assertEqual(type(format_spec), ast.JoinedStr)
|
||||
self.assertEqual(len(format_spec.values), 0)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_docstring(self):
|
||||
def f():
|
||||
f"""Not a docstring"""
|
||||
@@ -535,6 +531,8 @@ x = (
|
||||
exec(c)
|
||||
self.assertEqual(x[0], "foo3")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_compile_time_concat_errors(self):
|
||||
self.assertAllRaise(
|
||||
SyntaxError,
|
||||
@@ -550,6 +548,8 @@ x = (
|
||||
self.assertEqual(f"a", "a")
|
||||
self.assertEqual(f" ", " ")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_unterminated_string(self):
|
||||
self.assertAllRaise(
|
||||
SyntaxError,
|
||||
@@ -562,6 +562,8 @@ x = (
|
||||
],
|
||||
)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
|
||||
def test_mismatched_parens(self):
|
||||
self.assertAllRaise(
|
||||
@@ -666,6 +668,8 @@ x = (
|
||||
],
|
||||
)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_compile_time_concat(self):
|
||||
x = "def"
|
||||
self.assertEqual("abc" f"## {x}ghi", "abc## defghi")
|
||||
@@ -1516,6 +1520,8 @@ x = (
|
||||
],
|
||||
)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_del(self):
|
||||
self.assertAllRaise(
|
||||
SyntaxError,
|
||||
|
||||
+8
-2
@@ -67,6 +67,8 @@ class FutureTest(unittest.TestCase):
|
||||
from test.test_future_stmt import badsyntax_future5
|
||||
self.check_syntax_error(cm.exception, "badsyntax_future5", 4)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_badfuture6(self):
|
||||
with self.assertRaises(SyntaxError) as cm:
|
||||
from test.test_future_stmt import badsyntax_future6
|
||||
@@ -132,8 +134,6 @@ class FutureTest(unittest.TestCase):
|
||||
exec("from __future__ import unicode_literals; x = ''", {}, scope)
|
||||
self.assertIsInstance(scope["x"], str)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_syntactical_future_repl(self):
|
||||
p = spawn_python('-i')
|
||||
p.stdin.write(b"from __future__ import barry_as_FLUFL\n")
|
||||
@@ -198,6 +198,8 @@ class AnnotationsFutureTestCase(unittest.TestCase):
|
||||
)
|
||||
return scope
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_annotations(self):
|
||||
eq = self.assertAnnotationEqual
|
||||
eq('...')
|
||||
@@ -362,6 +364,8 @@ class AnnotationsFutureTestCase(unittest.TestCase):
|
||||
eq('(((a, b)))', '(a, b)')
|
||||
eq("1 + 2 + 3")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_fstring_debug_annotations(self):
|
||||
# f-strings with '=' don't round trip very well, so set the expected
|
||||
# result explicitly.
|
||||
@@ -372,6 +376,8 @@ class AnnotationsFutureTestCase(unittest.TestCase):
|
||||
self.assertAnnotationEqual("f'{x=!a}'", expected="f'x={x!a}'")
|
||||
self.assertAnnotationEqual("f'{x=!s:*^20}'", expected="f'x={x!s:*^20}'")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_infinity_numbers(self):
|
||||
inf = "1e" + repr(sys.float_info.max_10_exp + 1)
|
||||
infj = f"{inf}j"
|
||||
|
||||
Vendored
+6
@@ -12,6 +12,8 @@ class GlobalTests(unittest.TestCase):
|
||||
self.enterContext(check_warnings())
|
||||
warnings.filterwarnings("error", module="<test string>")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test1(self):
|
||||
prog_text_1 = """\
|
||||
def wrong1():
|
||||
@@ -22,6 +24,8 @@ def wrong1():
|
||||
"""
|
||||
check_syntax_error(self, prog_text_1, lineno=4, offset=5)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test2(self):
|
||||
prog_text_2 = """\
|
||||
def wrong2():
|
||||
@@ -30,6 +34,8 @@ def wrong2():
|
||||
"""
|
||||
check_syntax_error(self, prog_text_2, lineno=3, offset=5)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test3(self):
|
||||
prog_text_3 = """\
|
||||
def wrong3():
|
||||
|
||||
Vendored
+2
@@ -1062,6 +1062,8 @@ class GrammarTests(unittest.TestCase):
|
||||
self.assertEqual(g2(False), 0)
|
||||
self.assertEqual(g2(True), ('end', 1))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_yield(self):
|
||||
# Allowed as standalone statement
|
||||
def g(): yield 1
|
||||
|
||||
Vendored
-2
@@ -40,8 +40,6 @@ class KeywordOnlyArgTestCase(unittest.TestCase):
|
||||
compile(s, "<test>", "single")
|
||||
self.assertRaises(SyntaxError, shouldRaiseSyntaxError, codestr)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def testSyntaxErrorForFunctionDefinition(self):
|
||||
self.assertRaisesSyntaxError("def f(p, *):\n pass\n")
|
||||
self.assertRaisesSyntaxError("def f(p1, *, p1=100):\n pass\n")
|
||||
|
||||
Vendored
+30
-4
@@ -4,62 +4,80 @@ GLOBAL_VAR = None
|
||||
|
||||
class NamedExpressionInvalidTest(unittest.TestCase):
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_01(self):
|
||||
code = """x := 0"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_02(self):
|
||||
code = """x = y := 0"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_03(self):
|
||||
code = """y := f(x)"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_04(self):
|
||||
code = """y0 = y1 := f(x)"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # wrong error message
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_06(self):
|
||||
code = """((a, b) := (1, 2))"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "cannot use assignment expressions with tuple"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_07(self):
|
||||
code = """def spam(a = b := 42): pass"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_08(self):
|
||||
code = """def spam(a: b := 42 = 5): pass"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_09(self):
|
||||
code = """spam(a=b := 'c')"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_10(self):
|
||||
code = """spam(x = y := f(x))"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_11(self):
|
||||
code = """spam(a=1, b := 2)"""
|
||||
|
||||
@@ -67,6 +85,8 @@ class NamedExpressionInvalidTest(unittest.TestCase):
|
||||
"positional argument follows keyword argument"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_12(self):
|
||||
code = """spam(a=1, (b := 2))"""
|
||||
|
||||
@@ -74,6 +94,8 @@ class NamedExpressionInvalidTest(unittest.TestCase):
|
||||
"positional argument follows keyword argument"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_13(self):
|
||||
code = """spam(a=1, (b := 2))"""
|
||||
|
||||
@@ -81,14 +103,16 @@ class NamedExpressionInvalidTest(unittest.TestCase):
|
||||
"positional argument follows keyword argument"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_14(self):
|
||||
code = """(x := lambda: y := 1)"""
|
||||
|
||||
with self.assertRaisesRegex(SyntaxError, "invalid syntax"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # wrong error message
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_15(self):
|
||||
code = """(lambda: x := 1)"""
|
||||
|
||||
@@ -96,6 +120,8 @@ class NamedExpressionInvalidTest(unittest.TestCase):
|
||||
"cannot use assignment expressions with lambda"):
|
||||
exec(code, {}, {})
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_named_expression_invalid_16(self):
|
||||
code = "[i + 1 for i in i := [1,2]]"
|
||||
|
||||
|
||||
Vendored
+6
@@ -22,6 +22,8 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
||||
with self.assertRaisesRegex(SyntaxError, regex):
|
||||
compile(codestr + "\n", "<test>", "single")
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_invalid_syntax_errors(self):
|
||||
check_syntax_error(self, "def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
|
||||
check_syntax_error(self, "def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
|
||||
@@ -43,6 +45,8 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
||||
check_syntax_error(self, "def f(a, /, c, /, d, *, e): pass")
|
||||
check_syntax_error(self, "def f(a, *, c, /, d, e): pass")
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_invalid_syntax_errors_async(self):
|
||||
check_syntax_error(self, "async def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
|
||||
check_syntax_error(self, "async def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
|
||||
@@ -236,6 +240,8 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
||||
x = lambda a, b, /, : a + b
|
||||
self.assertEqual(x(1, 2), 3)
|
||||
|
||||
# TODO: RUSTPYTHON: wrong error message
|
||||
@unittest.expectedFailure
|
||||
def test_invalid_syntax_lambda(self):
|
||||
check_syntax_error(self, "lambda a, b = 5, /, c: None", "non-default argument follows default argument")
|
||||
check_syntax_error(self, "lambda a = 5, b, /, c: None", "non-default argument follows default argument")
|
||||
|
||||
Vendored
+18
-4
@@ -742,7 +742,8 @@ SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
|
||||
>>> f(x.y=1)
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
|
||||
>>> f((x)=2)
|
||||
# TODO: RUSTPYTHON
|
||||
>>> f((x)=2) # doctest: +SKIP
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
|
||||
>>> f(True=1)
|
||||
@@ -1649,7 +1650,8 @@ Corner-cases that used to crash:
|
||||
|
||||
Invalid pattern matching constructs:
|
||||
|
||||
>>> match ...:
|
||||
# TODO: RUSTPYTHON nothing raised.
|
||||
>>> match ...: # doctest: +SKIP
|
||||
... case 42 as _:
|
||||
... ...
|
||||
Traceback (most recent call last):
|
||||
@@ -1876,12 +1878,16 @@ class SyntaxTestCase(unittest.TestCase):
|
||||
offset=7
|
||||
)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_curly_brace_after_primary_raises_immediately(self):
|
||||
self._check_error("f{}", "invalid syntax", mode="single")
|
||||
|
||||
def test_assign_call(self):
|
||||
self._check_error("f() = 1", "assign")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_assign_del(self):
|
||||
self._check_error("del (,)", "invalid syntax")
|
||||
self._check_error("del 1", "cannot delete literal")
|
||||
@@ -1994,6 +2000,8 @@ class SyntaxTestCase(unittest.TestCase):
|
||||
"unindent does not match .* level",
|
||||
subclass=IndentationError)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_kwargs_last(self):
|
||||
self._check_error("int(base=10, '2')",
|
||||
"positional argument follows keyword argument")
|
||||
@@ -2005,6 +2013,8 @@ class SyntaxTestCase(unittest.TestCase):
|
||||
"positional argument follows "
|
||||
"keyword argument unpacking")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_kwargs_last3(self):
|
||||
self._check_error("int(**{'base': 10}, *['2'])",
|
||||
"iterable argument unpacking follows "
|
||||
@@ -2073,8 +2083,6 @@ if x:
|
||||
code += f"{' '*4*12}pass"
|
||||
self._check_error(code, "too many statically nested blocks")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_barry_as_flufl_with_syntax_errors(self):
|
||||
# The "barry_as_flufl" rule can produce some "bugs-at-a-distance" if
|
||||
# is reading the wrong token in the presence of syntax errors later
|
||||
@@ -2092,6 +2100,8 @@ def func2():
|
||||
"""
|
||||
self._check_error(code, "expected ':'")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_invalid_line_continuation_error_position(self):
|
||||
self._check_error(r"a = 3 \ 4",
|
||||
"unexpected character after line continuation character",
|
||||
@@ -2103,6 +2113,8 @@ def func2():
|
||||
"unexpected character after line continuation character",
|
||||
lineno=3, offset=4)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_invalid_line_continuation_left_recursive(self):
|
||||
# Check bpo-42218: SyntaxErrors following left-recursive rules
|
||||
# (t_primary_raw in this case) need to be tested explicitly
|
||||
@@ -2146,6 +2158,8 @@ case(34)
|
||||
"""
|
||||
compile(code, "<string>", "exec")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_multiline_compiler_error_points_to_the_end(self):
|
||||
self._check_error(
|
||||
"call(\na=1,\na=1\n)",
|
||||
|
||||
Vendored
-2
@@ -91,8 +91,6 @@ class TestTimeit(unittest.TestCase):
|
||||
self.assertRaises(SyntaxError, timeit.Timer, setup='from timeit import *')
|
||||
self.assertRaises(SyntaxError, timeit.Timer, setup=' pass')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_timer_empty_stmt(self):
|
||||
timeit.Timer(stmt='')
|
||||
timeit.Timer(stmt=' \n\t\f')
|
||||
|
||||
Vendored
-4
@@ -2,8 +2,6 @@ import unittest
|
||||
|
||||
class PEP3131Test(unittest.TestCase):
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_valid(self):
|
||||
class T:
|
||||
ä = 1
|
||||
@@ -15,8 +13,6 @@ class PEP3131Test(unittest.TestCase):
|
||||
self.assertEqual(getattr(T, '\u87d2'), 3)
|
||||
self.assertEqual(getattr(T, 'x\U000E0100'), 4)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_non_bmp_normalized(self):
|
||||
𝔘𝔫𝔦𝔠𝔬𝔡𝔢 = 1
|
||||
self.assertIn("Unicode", dir())
|
||||
|
||||
+7
-1
@@ -11,7 +11,13 @@ license.workspace = true
|
||||
[dependencies]
|
||||
rustpython-codegen = { workspace = true }
|
||||
rustpython-compiler-core = { workspace = true }
|
||||
rustpython-parser = { workspace = true }
|
||||
rustpython-compiler-source = { workspace = true }
|
||||
# rustpython-parser = { workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ruff_source_file = { workspace = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -10,10 +10,16 @@ license.workspace = true
|
||||
|
||||
|
||||
[dependencies]
|
||||
rustpython-ast = { workspace = true, features=["unparse", "constant-optimization"] }
|
||||
# rustpython-ast = { workspace = true, features=["unparse", "constant-optimization"] }
|
||||
rustpython-common = { workspace = true }
|
||||
rustpython-parser-core = { workspace = true }
|
||||
# rustpython-parser-core = { workspace = true }
|
||||
rustpython-compiler-core = { workspace = true }
|
||||
rustpython-compiler-source = {workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
ruff_source_file = { workspace = true }
|
||||
ruff_python_codegen = { workspace = true }
|
||||
|
||||
ahash = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
@@ -22,11 +28,13 @@ itertools = { workspace = true }
|
||||
log = { workspace = true }
|
||||
num-complex = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
malachite-bigint = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustpython-parser = { workspace = true }
|
||||
# rustpython-parser = { workspace = true }
|
||||
|
||||
insta = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
workspace = true
|
||||
+822
-601
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,23 @@
|
||||
use ruff_source_file::SourceLocation;
|
||||
use std::fmt;
|
||||
use thiserror::Error;
|
||||
|
||||
pub type CodegenError = rustpython_parser_core::source_code::LocatedError<CodegenErrorType>;
|
||||
// pub type CodegenError = rustpython_parser_core::source_code::LocatedError<CodegenErrorType>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub struct CodegenError {
|
||||
pub location: Option<SourceLocation>,
|
||||
#[source]
|
||||
pub error: CodegenErrorType,
|
||||
pub source_path: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for CodegenError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO:
|
||||
self.error.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::ops;
|
||||
|
||||
use crate::IndexSet;
|
||||
use ruff_source_file::{OneIndexed, SourceLocation};
|
||||
use rustpython_compiler_core::bytecode::{
|
||||
CodeFlags, CodeObject, CodeUnit, ConstantData, InstrDisplayContext, Instruction, Label, OpArg,
|
||||
};
|
||||
use rustpython_parser_core::source_code::{LineNumber, SourceLocation};
|
||||
// use rustpython_parser_core::source_code::{LineNumber, SourceLocation};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct BlockIdx(pub u32);
|
||||
@@ -37,11 +38,12 @@ impl ops::IndexMut<BlockIdx> for Vec<Block> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InstructionInfo {
|
||||
pub instr: Instruction,
|
||||
pub arg: OpArg,
|
||||
pub target: BlockIdx,
|
||||
// pub range: TextRange,
|
||||
pub location: SourceLocation,
|
||||
}
|
||||
|
||||
@@ -68,7 +70,7 @@ pub struct CodeInfo {
|
||||
pub arg_count: u32,
|
||||
pub kwonlyarg_count: u32,
|
||||
pub source_path: String,
|
||||
pub first_line_number: LineNumber,
|
||||
pub first_line_number: OneIndexed,
|
||||
pub obj_name: String, // Name of the object that created this code object
|
||||
|
||||
pub blocks: Vec<Block>,
|
||||
@@ -134,7 +136,8 @@ impl CodeInfo {
|
||||
*arg = new_arg;
|
||||
}
|
||||
let (extras, lo_arg) = arg.split();
|
||||
locations.extend(std::iter::repeat(info.location).take(arg.instr_size()));
|
||||
locations
|
||||
.extend(std::iter::repeat(info.location.clone()).take(arg.instr_size()));
|
||||
instructions.extend(
|
||||
extras
|
||||
.map(|byte| CodeUnit::new(Instruction::ExtendedArg, byte))
|
||||
|
||||
@@ -14,3 +14,49 @@ pub mod ir;
|
||||
pub mod symboltable;
|
||||
|
||||
pub use compile::CompileOpts;
|
||||
use ruff_python_ast::Expr;
|
||||
|
||||
pub trait ToPythonName {
|
||||
/// Returns a short name for the node suitable for use in error messages.
|
||||
fn python_name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
impl ToPythonName for Expr {
|
||||
fn python_name(&self) -> &'static str {
|
||||
match self {
|
||||
Expr::BoolOp { .. } | Expr::BinOp { .. } | Expr::UnaryOp { .. } => "operator",
|
||||
Expr::Subscript { .. } => "subscript",
|
||||
Expr::Await { .. } => "await expression",
|
||||
Expr::Yield { .. } | Expr::YieldFrom { .. } => "yield expression",
|
||||
Expr::Compare { .. } => "comparison",
|
||||
Expr::Attribute { .. } => "attribute",
|
||||
Expr::Call { .. } => "function call",
|
||||
Expr::BooleanLiteral(b) => {
|
||||
if b.value {
|
||||
"True"
|
||||
} else {
|
||||
"False"
|
||||
}
|
||||
}
|
||||
Expr::EllipsisLiteral(_) => "ellipsis",
|
||||
Expr::NoneLiteral(_) => "None",
|
||||
Expr::NumberLiteral(_) | Expr::BytesLiteral(_) | Expr::StringLiteral(_) => "literal",
|
||||
Expr::Tuple(_) => "tuple",
|
||||
Expr::List { .. } => "list",
|
||||
Expr::Dict { .. } => "dict display",
|
||||
Expr::Set { .. } => "set display",
|
||||
Expr::ListComp { .. } => "list comprehension",
|
||||
Expr::DictComp { .. } => "dict comprehension",
|
||||
Expr::SetComp { .. } => "set comprehension",
|
||||
Expr::Generator { .. } => "generator expression",
|
||||
Expr::Starred { .. } => "starred",
|
||||
Expr::Slice { .. } => "slice",
|
||||
Expr::FString { .. } => "f-string expression",
|
||||
Expr::Name { .. } => "name",
|
||||
Expr::Lambda { .. } => "lambda",
|
||||
Expr::If { .. } => "conditional expression",
|
||||
Expr::Named { .. } => "named expression",
|
||||
Expr::IpyEscapeCommand(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
+275
-188
@@ -12,8 +12,16 @@ use crate::{
|
||||
error::{CodegenError, CodegenErrorType},
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
use rustpython_ast::{self as ast, located::Located};
|
||||
use rustpython_parser_core::source_code::{LineNumber, SourceLocation};
|
||||
use ruff_python_ast::{
|
||||
self as ast, Comprehension, Decorator, Expr, Identifier, ModExpression, ModModule, Parameter,
|
||||
ParameterWithDefault, Parameters, Pattern, PatternMatchAs, PatternMatchClass,
|
||||
PatternMatchMapping, PatternMatchOr, PatternMatchSequence, PatternMatchStar, PatternMatchValue,
|
||||
Stmt, TypeParam, TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, TypeParams,
|
||||
};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use rustpython_compiler_source::{SourceCode, SourceLocation};
|
||||
// use rustpython_ast::{self as ast, located::Located};
|
||||
// use rustpython_parser_core::source_code::{LineNumber, SourceLocation};
|
||||
use std::{borrow::Cow, fmt};
|
||||
|
||||
/// Captures all symbols in the current scope, and has a list of sub-scopes in this scope.
|
||||
@@ -51,15 +59,18 @@ impl SymbolTable {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scan_program(program: &[ast::located::Stmt]) -> SymbolTableResult<Self> {
|
||||
let mut builder = SymbolTableBuilder::new();
|
||||
builder.scan_statements(program)?;
|
||||
pub fn scan_program(
|
||||
program: &ModModule,
|
||||
source_code: SourceCode<'_>,
|
||||
) -> SymbolTableResult<Self> {
|
||||
let mut builder = SymbolTableBuilder::new(source_code);
|
||||
builder.scan_statements(program.body.as_ref())?;
|
||||
builder.finish()
|
||||
}
|
||||
|
||||
pub fn scan_expr(expr: &ast::located::Expr) -> SymbolTableResult<Self> {
|
||||
let mut builder = SymbolTableBuilder::new();
|
||||
builder.scan_expression(expr, ExpressionContext::Load)?;
|
||||
pub fn scan_expr(expr: &ModExpression, source_code: SourceCode<'_>) -> SymbolTableResult<Self> {
|
||||
let mut builder = SymbolTableBuilder::new(source_code);
|
||||
builder.scan_expression(expr.body.as_ref(), ExpressionContext::Load)?;
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
@@ -176,11 +187,8 @@ pub struct SymbolTableError {
|
||||
impl SymbolTableError {
|
||||
pub fn into_codegen_error(self, source_path: String) -> CodegenError {
|
||||
CodegenError {
|
||||
location: self.location,
|
||||
error: CodegenErrorType::SyntaxError(self.error),
|
||||
location: self.location.map(|l| SourceLocation {
|
||||
row: l.row,
|
||||
column: l.column,
|
||||
}),
|
||||
source_path,
|
||||
}
|
||||
}
|
||||
@@ -551,11 +559,12 @@ enum SymbolUsage {
|
||||
Iter,
|
||||
}
|
||||
|
||||
struct SymbolTableBuilder {
|
||||
struct SymbolTableBuilder<'src> {
|
||||
class_name: Option<String>,
|
||||
// Scope stack.
|
||||
tables: Vec<SymbolTable>,
|
||||
future_annotations: bool,
|
||||
source_code: SourceCode<'src>,
|
||||
}
|
||||
|
||||
/// Enum to indicate in what mode an expression
|
||||
@@ -571,19 +580,20 @@ enum ExpressionContext {
|
||||
IterDefinitionExp,
|
||||
}
|
||||
|
||||
impl SymbolTableBuilder {
|
||||
fn new() -> Self {
|
||||
impl<'src> SymbolTableBuilder<'src> {
|
||||
fn new(source_code: SourceCode<'src>) -> Self {
|
||||
let mut this = Self {
|
||||
class_name: None,
|
||||
tables: vec![],
|
||||
future_annotations: false,
|
||||
source_code,
|
||||
};
|
||||
this.enter_scope("top", SymbolTableType::Module, 0);
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolTableBuilder {
|
||||
impl SymbolTableBuilder<'_> {
|
||||
fn finish(mut self) -> Result<SymbolTable, SymbolTableError> {
|
||||
assert_eq!(self.tables.len(), 1);
|
||||
let mut symbol_table = self.tables.pop().unwrap();
|
||||
@@ -607,38 +617,34 @@ impl SymbolTableBuilder {
|
||||
self.tables.last_mut().unwrap().sub_tables.push(table);
|
||||
}
|
||||
|
||||
fn scan_statements(&mut self, statements: &[ast::located::Stmt]) -> SymbolTableResult {
|
||||
fn line_index_start(&self, range: TextRange) -> u32 {
|
||||
self.source_code.line_index(range.start()).get() as _
|
||||
}
|
||||
|
||||
fn scan_statements(&mut self, statements: &[Stmt]) -> SymbolTableResult {
|
||||
for statement in statements {
|
||||
self.scan_statement(statement)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_parameters(
|
||||
&mut self,
|
||||
parameters: &[ast::located::ArgWithDefault],
|
||||
) -> SymbolTableResult {
|
||||
fn scan_parameters(&mut self, parameters: &[ParameterWithDefault]) -> SymbolTableResult {
|
||||
for parameter in parameters {
|
||||
let usage = if parameter.def.annotation.is_some() {
|
||||
SymbolUsage::AnnotationParameter
|
||||
} else {
|
||||
SymbolUsage::Parameter
|
||||
};
|
||||
self.register_name(parameter.def.arg.as_str(), usage, parameter.def.location())?;
|
||||
self.scan_parameter(¶meter.parameter)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_parameter(&mut self, parameter: &ast::located::Arg) -> SymbolTableResult {
|
||||
fn scan_parameter(&mut self, parameter: &Parameter) -> SymbolTableResult {
|
||||
let usage = if parameter.annotation.is_some() {
|
||||
SymbolUsage::AnnotationParameter
|
||||
} else {
|
||||
SymbolUsage::Parameter
|
||||
};
|
||||
self.register_name(parameter.arg.as_str(), usage, parameter.location())
|
||||
self.register_ident(¶meter.name, usage)
|
||||
}
|
||||
|
||||
fn scan_annotation(&mut self, annotation: &ast::located::Expr) -> SymbolTableResult {
|
||||
fn scan_annotation(&mut self, annotation: &Expr) -> SymbolTableResult {
|
||||
if self.future_annotations {
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -646,8 +652,8 @@ impl SymbolTableBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_statement(&mut self, statement: &ast::located::Stmt) -> SymbolTableResult {
|
||||
use ast::located::*;
|
||||
fn scan_statement(&mut self, statement: &Stmt) -> SymbolTableResult {
|
||||
use ruff_python_ast::*;
|
||||
if let Stmt::ImportFrom(StmtImportFrom { module, names, .. }) = &statement {
|
||||
if module.as_ref().map(|id| id.as_str()) == Some("__future__") {
|
||||
for feature in names {
|
||||
@@ -658,101 +664,109 @@ impl SymbolTableBuilder {
|
||||
}
|
||||
}
|
||||
match &statement {
|
||||
Stmt::Global(StmtGlobal { names, range }) => {
|
||||
Stmt::Global(StmtGlobal { names, .. }) => {
|
||||
for name in names {
|
||||
self.register_name(name.as_str(), SymbolUsage::Global, range.start)?;
|
||||
self.register_ident(name, SymbolUsage::Global)?;
|
||||
}
|
||||
}
|
||||
Stmt::Nonlocal(StmtNonlocal { names, range }) => {
|
||||
Stmt::Nonlocal(StmtNonlocal { names, .. }) => {
|
||||
for name in names {
|
||||
self.register_name(name.as_str(), SymbolUsage::Nonlocal, range.start)?;
|
||||
self.register_ident(name, SymbolUsage::Nonlocal)?;
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(StmtFunctionDef {
|
||||
name,
|
||||
body,
|
||||
args,
|
||||
decorator_list,
|
||||
type_params,
|
||||
returns,
|
||||
range,
|
||||
..
|
||||
})
|
||||
| Stmt::AsyncFunctionDef(StmtAsyncFunctionDef {
|
||||
name,
|
||||
body,
|
||||
args,
|
||||
parameters,
|
||||
decorator_list,
|
||||
type_params,
|
||||
returns,
|
||||
range,
|
||||
..
|
||||
}) => {
|
||||
self.scan_expressions(decorator_list, ExpressionContext::Load)?;
|
||||
self.register_name(name.as_str(), SymbolUsage::Assigned, range.start)?;
|
||||
self.scan_decorators(decorator_list, ExpressionContext::Load)?;
|
||||
self.register_ident(name, SymbolUsage::Assigned)?;
|
||||
if let Some(expression) = returns {
|
||||
self.scan_annotation(expression)?;
|
||||
}
|
||||
if !type_params.is_empty() {
|
||||
if let Some(type_params) = type_params {
|
||||
self.enter_scope(
|
||||
&format!("<generic parameters of {}>", name.as_str()),
|
||||
SymbolTableType::TypeParams,
|
||||
range.start.row.get(),
|
||||
// FIXME: line no
|
||||
self.line_index_start(*range),
|
||||
);
|
||||
self.scan_type_params(type_params)?;
|
||||
}
|
||||
self.enter_function(name.as_str(), args, range.start.row)?;
|
||||
self.enter_scope_with_parameters(
|
||||
name.as_str(),
|
||||
parameters,
|
||||
self.line_index_start(*range),
|
||||
)?;
|
||||
self.scan_statements(body)?;
|
||||
self.leave_scope();
|
||||
if !type_params.is_empty() {
|
||||
if type_params.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
}
|
||||
Stmt::ClassDef(StmtClassDef {
|
||||
name,
|
||||
body,
|
||||
bases,
|
||||
keywords,
|
||||
arguments,
|
||||
decorator_list,
|
||||
type_params,
|
||||
range,
|
||||
}) => {
|
||||
if !type_params.is_empty() {
|
||||
if let Some(type_params) = type_params {
|
||||
self.enter_scope(
|
||||
&format!("<generic parameters of {}>", name.as_str()),
|
||||
SymbolTableType::TypeParams,
|
||||
range.start.row.get(),
|
||||
self.line_index_start(type_params.range),
|
||||
);
|
||||
self.scan_type_params(type_params)?;
|
||||
}
|
||||
self.enter_scope(name.as_str(), SymbolTableType::Class, range.start.row.get());
|
||||
self.enter_scope(
|
||||
name.as_str(),
|
||||
SymbolTableType::Class,
|
||||
self.line_index_start(*range),
|
||||
);
|
||||
let prev_class = std::mem::replace(&mut self.class_name, Some(name.to_string()));
|
||||
self.register_name("__module__", SymbolUsage::Assigned, range.start)?;
|
||||
self.register_name("__qualname__", SymbolUsage::Assigned, range.start)?;
|
||||
self.register_name("__doc__", SymbolUsage::Assigned, range.start)?;
|
||||
self.register_name("__class__", SymbolUsage::Assigned, range.start)?;
|
||||
self.register_name("__module__", SymbolUsage::Assigned, *range)?;
|
||||
self.register_name("__qualname__", SymbolUsage::Assigned, *range)?;
|
||||
self.register_name("__doc__", SymbolUsage::Assigned, *range)?;
|
||||
self.register_name("__class__", SymbolUsage::Assigned, *range)?;
|
||||
self.scan_statements(body)?;
|
||||
self.leave_scope();
|
||||
self.class_name = prev_class;
|
||||
self.scan_expressions(bases, ExpressionContext::Load)?;
|
||||
for keyword in keywords {
|
||||
self.scan_expression(&keyword.value, ExpressionContext::Load)?;
|
||||
if let Some(arguments) = arguments {
|
||||
self.scan_expressions(&arguments.args, ExpressionContext::Load)?;
|
||||
for keyword in &arguments.keywords {
|
||||
self.scan_expression(&keyword.value, ExpressionContext::Load)?;
|
||||
}
|
||||
}
|
||||
if !type_params.is_empty() {
|
||||
if type_params.is_some() {
|
||||
self.leave_scope();
|
||||
}
|
||||
self.scan_expressions(decorator_list, ExpressionContext::Load)?;
|
||||
self.register_name(name.as_str(), SymbolUsage::Assigned, range.start)?;
|
||||
self.scan_decorators(decorator_list, ExpressionContext::Load)?;
|
||||
self.register_ident(name, SymbolUsage::Assigned)?;
|
||||
}
|
||||
Stmt::Expr(StmtExpr { value, .. }) => {
|
||||
self.scan_expression(value, ExpressionContext::Load)?
|
||||
}
|
||||
Stmt::If(StmtIf {
|
||||
test, body, orelse, ..
|
||||
test,
|
||||
body,
|
||||
elif_else_clauses,
|
||||
..
|
||||
}) => {
|
||||
self.scan_expression(test, ExpressionContext::Load)?;
|
||||
self.scan_statements(body)?;
|
||||
self.scan_statements(orelse)?;
|
||||
for elif in elif_else_clauses {
|
||||
if let Some(test) = &elif.test {
|
||||
self.scan_expression(test, ExpressionContext::Load)?;
|
||||
}
|
||||
self.scan_statements(&elif.body)?;
|
||||
}
|
||||
}
|
||||
Stmt::For(StmtFor {
|
||||
target,
|
||||
@@ -760,13 +774,6 @@ impl SymbolTableBuilder {
|
||||
body,
|
||||
orelse,
|
||||
..
|
||||
})
|
||||
| Stmt::AsyncFor(StmtAsyncFor {
|
||||
target,
|
||||
iter,
|
||||
body,
|
||||
orelse,
|
||||
..
|
||||
}) => {
|
||||
self.scan_expression(target, ExpressionContext::Store)?;
|
||||
self.scan_expression(iter, ExpressionContext::Load)?;
|
||||
@@ -783,18 +790,18 @@ impl SymbolTableBuilder {
|
||||
Stmt::Break(_) | Stmt::Continue(_) | Stmt::Pass(_) => {
|
||||
// No symbols here.
|
||||
}
|
||||
Stmt::Import(StmtImport { names, range })
|
||||
| Stmt::ImportFrom(StmtImportFrom { names, range, .. }) => {
|
||||
Stmt::Import(StmtImport { names, .. })
|
||||
| Stmt::ImportFrom(StmtImportFrom { names, .. }) => {
|
||||
for name in names {
|
||||
if let Some(alias) = &name.asname {
|
||||
// `import my_module as my_alias`
|
||||
self.register_name(alias.as_str(), SymbolUsage::Imported, range.start)?;
|
||||
self.register_ident(alias, SymbolUsage::Imported)?;
|
||||
} else {
|
||||
// `import module`
|
||||
self.register_name(
|
||||
name.name.split('.').next().unwrap(),
|
||||
SymbolUsage::Imported,
|
||||
range.start,
|
||||
name.name.range,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -831,11 +838,7 @@ impl SymbolTableBuilder {
|
||||
// https://github.com/python/cpython/blob/main/Python/symtable.c#L1233
|
||||
match &**target {
|
||||
Expr::Name(ast::ExprName { id, .. }) if *simple => {
|
||||
self.register_name(
|
||||
id.as_str(),
|
||||
SymbolUsage::AnnotationAssigned,
|
||||
range.start,
|
||||
)?;
|
||||
self.register_name(id.as_str(), SymbolUsage::AnnotationAssigned, *range)?;
|
||||
}
|
||||
_ => {
|
||||
self.scan_expression(target, ExpressionContext::Store)?;
|
||||
@@ -846,8 +849,7 @@ impl SymbolTableBuilder {
|
||||
self.scan_expression(value, ExpressionContext::Load)?;
|
||||
}
|
||||
}
|
||||
Stmt::With(StmtWith { items, body, .. })
|
||||
| Stmt::AsyncWith(StmtAsyncWith { items, body, .. }) => {
|
||||
Stmt::With(StmtWith { items, body, .. }) => {
|
||||
for item in items {
|
||||
self.scan_expression(&item.context_expr, ExpressionContext::Load)?;
|
||||
if let Some(expression) = &item.optional_vars {
|
||||
@@ -861,14 +863,7 @@ impl SymbolTableBuilder {
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
range,
|
||||
})
|
||||
| Stmt::TryStar(StmtTryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
range,
|
||||
..
|
||||
}) => {
|
||||
self.scan_statements(body)?;
|
||||
for handler in handlers {
|
||||
@@ -882,7 +877,7 @@ impl SymbolTableBuilder {
|
||||
self.scan_expression(expression, ExpressionContext::Load)?;
|
||||
}
|
||||
if let Some(name) = name {
|
||||
self.register_name(name.as_str(), SymbolUsage::Assigned, range.start)?;
|
||||
self.register_ident(name, SymbolUsage::Assigned)?;
|
||||
}
|
||||
self.scan_statements(body)?;
|
||||
}
|
||||
@@ -892,8 +887,10 @@ impl SymbolTableBuilder {
|
||||
Stmt::Match(StmtMatch { subject, cases, .. }) => {
|
||||
self.scan_expression(subject, ExpressionContext::Load)?;
|
||||
for case in cases {
|
||||
// TODO: below
|
||||
// self.scan_pattern(&case.pattern, ExpressionContext::Load)?;
|
||||
self.scan_pattern(&case.pattern)?;
|
||||
if let Some(guard) = &case.guard {
|
||||
self.scan_expression(guard, ExpressionContext::Load)?;
|
||||
}
|
||||
self.scan_statements(&case.body)?;
|
||||
}
|
||||
}
|
||||
@@ -909,13 +906,14 @@ impl SymbolTableBuilder {
|
||||
name,
|
||||
value,
|
||||
type_params,
|
||||
range,
|
||||
..
|
||||
}) => {
|
||||
if !type_params.is_empty() {
|
||||
if let Some(type_params) = type_params {
|
||||
self.enter_scope(
|
||||
&name.to_string(),
|
||||
// &name.to_string(),
|
||||
"TypeAlias",
|
||||
SymbolTableType::TypeParams,
|
||||
range.start.row.get(),
|
||||
self.line_index_start(type_params.range),
|
||||
);
|
||||
self.scan_type_params(type_params)?;
|
||||
self.scan_expression(value, ExpressionContext::Load)?;
|
||||
@@ -925,13 +923,25 @@ impl SymbolTableBuilder {
|
||||
}
|
||||
self.scan_expression(name, ExpressionContext::Store)?;
|
||||
}
|
||||
Stmt::IpyEscapeCommand(_) => todo!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_decorators(
|
||||
&mut self,
|
||||
decorators: &[Decorator],
|
||||
context: ExpressionContext,
|
||||
) -> SymbolTableResult {
|
||||
for decorator in decorators {
|
||||
self.scan_expression(&decorator.expression, context)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_expressions(
|
||||
&mut self,
|
||||
expressions: &[ast::located::Expr],
|
||||
expressions: &[Expr],
|
||||
context: ExpressionContext,
|
||||
) -> SymbolTableResult {
|
||||
for expression in expressions {
|
||||
@@ -942,10 +952,10 @@ impl SymbolTableBuilder {
|
||||
|
||||
fn scan_expression(
|
||||
&mut self,
|
||||
expression: &ast::located::Expr,
|
||||
expression: &Expr,
|
||||
context: ExpressionContext,
|
||||
) -> SymbolTableResult {
|
||||
use ast::located::*;
|
||||
use ruff_python_ast::*;
|
||||
match expression {
|
||||
Expr::BinOp(ExprBinOp {
|
||||
left,
|
||||
@@ -984,16 +994,12 @@ impl SymbolTableBuilder {
|
||||
}) => {
|
||||
self.scan_expression(value, ExpressionContext::Load)?;
|
||||
}
|
||||
Expr::Dict(ExprDict {
|
||||
keys,
|
||||
values,
|
||||
range: _,
|
||||
}) => {
|
||||
for (key, value) in keys.iter().zip(values.iter()) {
|
||||
if let Some(key) = key {
|
||||
Expr::Dict(ExprDict { items, range: _ }) => {
|
||||
for item in items {
|
||||
if let Some(key) = &item.key {
|
||||
self.scan_expression(key, context)?;
|
||||
}
|
||||
self.scan_expression(value, context)?;
|
||||
self.scan_expression(&item.value, context)?;
|
||||
}
|
||||
}
|
||||
Expr::Await(ExprAwait { value, range: _ }) => {
|
||||
@@ -1012,7 +1018,6 @@ impl SymbolTableBuilder {
|
||||
}) => {
|
||||
self.scan_expression(operand, context)?;
|
||||
}
|
||||
Expr::Constant(ExprConstant { range: _, .. }) => {}
|
||||
Expr::Starred(ExprStarred {
|
||||
value, range: _, ..
|
||||
}) => {
|
||||
@@ -1039,26 +1044,27 @@ impl SymbolTableBuilder {
|
||||
self.scan_expression(step, context)?;
|
||||
}
|
||||
}
|
||||
Expr::GeneratorExp(ExprGeneratorExp {
|
||||
Expr::Generator(ExprGenerator {
|
||||
elt,
|
||||
generators,
|
||||
range,
|
||||
..
|
||||
}) => {
|
||||
self.scan_comprehension("genexpr", elt, None, generators, range.start)?;
|
||||
self.scan_comprehension("genexpr", elt, None, generators, *range)?;
|
||||
}
|
||||
Expr::ListComp(ExprListComp {
|
||||
elt,
|
||||
generators,
|
||||
range,
|
||||
}) => {
|
||||
self.scan_comprehension("genexpr", elt, None, generators, range.start)?;
|
||||
self.scan_comprehension("genexpr", elt, None, generators, *range)?;
|
||||
}
|
||||
Expr::SetComp(ExprSetComp {
|
||||
elt,
|
||||
generators,
|
||||
range,
|
||||
}) => {
|
||||
self.scan_comprehension("genexpr", elt, None, generators, range.start)?;
|
||||
self.scan_comprehension("genexpr", elt, None, generators, *range)?;
|
||||
}
|
||||
Expr::DictComp(ExprDictComp {
|
||||
key,
|
||||
@@ -1066,12 +1072,11 @@ impl SymbolTableBuilder {
|
||||
generators,
|
||||
range,
|
||||
}) => {
|
||||
self.scan_comprehension("genexpr", key, Some(value), generators, range.start)?;
|
||||
self.scan_comprehension("genexpr", key, Some(value), generators, *range)?;
|
||||
}
|
||||
Expr::Call(ExprCall {
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
arguments,
|
||||
range: _,
|
||||
}) => {
|
||||
match context {
|
||||
@@ -1083,43 +1088,27 @@ impl SymbolTableBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
self.scan_expressions(args, ExpressionContext::Load)?;
|
||||
for keyword in keywords {
|
||||
self.scan_expressions(&arguments.args, ExpressionContext::Load)?;
|
||||
for keyword in &arguments.keywords {
|
||||
self.scan_expression(&keyword.value, ExpressionContext::Load)?;
|
||||
}
|
||||
}
|
||||
Expr::FormattedValue(ExprFormattedValue {
|
||||
value,
|
||||
format_spec,
|
||||
range: _,
|
||||
..
|
||||
}) => {
|
||||
self.scan_expression(value, ExpressionContext::Load)?;
|
||||
if let Some(spec) = format_spec {
|
||||
self.scan_expression(spec, ExpressionContext::Load)?;
|
||||
}
|
||||
}
|
||||
Expr::JoinedStr(ExprJoinedStr { values, range: _ }) => {
|
||||
for value in values {
|
||||
self.scan_expression(value, ExpressionContext::Load)?;
|
||||
}
|
||||
}
|
||||
Expr::Name(ExprName { id, range, .. }) => {
|
||||
let id = id.as_str();
|
||||
// Determine the contextual usage of this symbol:
|
||||
match context {
|
||||
ExpressionContext::Delete => {
|
||||
self.register_name(id, SymbolUsage::Assigned, range.start)?;
|
||||
self.register_name(id, SymbolUsage::Used, range.start)?;
|
||||
self.register_name(id, SymbolUsage::Assigned, *range)?;
|
||||
self.register_name(id, SymbolUsage::Used, *range)?;
|
||||
}
|
||||
ExpressionContext::Load | ExpressionContext::IterDefinitionExp => {
|
||||
self.register_name(id, SymbolUsage::Used, range.start)?;
|
||||
self.register_name(id, SymbolUsage::Used, *range)?;
|
||||
}
|
||||
ExpressionContext::Store => {
|
||||
self.register_name(id, SymbolUsage::Assigned, range.start)?;
|
||||
self.register_name(id, SymbolUsage::Assigned, *range)?;
|
||||
}
|
||||
ExpressionContext::Iter => {
|
||||
self.register_name(id, SymbolUsage::Iter, range.start)?;
|
||||
self.register_name(id, SymbolUsage::Iter, *range)?;
|
||||
}
|
||||
}
|
||||
// Interesting stuff about the __class__ variable:
|
||||
@@ -1128,15 +1117,27 @@ impl SymbolTableBuilder {
|
||||
&& self.tables.last().unwrap().typ == SymbolTableType::Function
|
||||
&& id == "super"
|
||||
{
|
||||
self.register_name("__class__", SymbolUsage::Used, range.start)?;
|
||||
self.register_name("__class__", SymbolUsage::Used, *range)?;
|
||||
}
|
||||
}
|
||||
Expr::Lambda(ExprLambda {
|
||||
args,
|
||||
body,
|
||||
parameters,
|
||||
range: _,
|
||||
}) => {
|
||||
self.enter_function("lambda", args, expression.location().row)?;
|
||||
if let Some(parameters) = parameters {
|
||||
self.enter_scope_with_parameters(
|
||||
"lambda",
|
||||
parameters,
|
||||
self.line_index_start(expression.range()),
|
||||
)?;
|
||||
} else {
|
||||
self.enter_scope(
|
||||
"lambda",
|
||||
SymbolTableType::Function,
|
||||
self.line_index_start(expression.range()),
|
||||
);
|
||||
}
|
||||
match context {
|
||||
ExpressionContext::IterDefinitionExp => {
|
||||
self.scan_expression(body, ExpressionContext::IterDefinitionExp)?;
|
||||
@@ -1147,7 +1148,25 @@ impl SymbolTableBuilder {
|
||||
}
|
||||
self.leave_scope();
|
||||
}
|
||||
Expr::IfExp(ExprIfExp {
|
||||
Expr::FString(ExprFString { value, .. }) => {
|
||||
for expr in value.elements().filter_map(|x| x.as_expression()) {
|
||||
self.scan_expression(&expr.expression, ExpressionContext::Load)?;
|
||||
if let Some(format_spec) = &expr.format_spec {
|
||||
for element in format_spec.elements.expressions() {
|
||||
self.scan_expression(&element.expression, ExpressionContext::Load)?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Constants
|
||||
Expr::StringLiteral(_)
|
||||
| Expr::BytesLiteral(_)
|
||||
| Expr::NumberLiteral(_)
|
||||
| Expr::BooleanLiteral(_)
|
||||
| Expr::NoneLiteral(_)
|
||||
| Expr::EllipsisLiteral(_) => {}
|
||||
Expr::IpyEscapeCommand(_) => todo!(),
|
||||
Expr::If(ExprIf {
|
||||
test,
|
||||
body,
|
||||
orelse,
|
||||
@@ -1158,7 +1177,7 @@ impl SymbolTableBuilder {
|
||||
self.scan_expression(orelse, ExpressionContext::Load)?;
|
||||
}
|
||||
|
||||
Expr::NamedExpr(ExprNamedExpr {
|
||||
Expr::Named(ExprNamed {
|
||||
target,
|
||||
value,
|
||||
range,
|
||||
@@ -1167,9 +1186,9 @@ impl SymbolTableBuilder {
|
||||
// comprehension iterator definitions
|
||||
if let ExpressionContext::IterDefinitionExp = context {
|
||||
return Err(SymbolTableError {
|
||||
error: "assignment expression cannot be used in a comprehension iterable expression".to_string(),
|
||||
location: Some(target.location()),
|
||||
});
|
||||
error: "assignment expression cannot be used in a comprehension iterable expression".to_string(),
|
||||
location: Some(self.source_code.source_location(target.range().start())),
|
||||
});
|
||||
}
|
||||
|
||||
self.scan_expression(value, ExpressionContext::Load)?;
|
||||
@@ -1185,13 +1204,13 @@ impl SymbolTableBuilder {
|
||||
self.register_name(
|
||||
id,
|
||||
SymbolUsage::AssignedNamedExprInComprehension,
|
||||
range.start,
|
||||
*range,
|
||||
)?;
|
||||
} else {
|
||||
// omit one recursion. When the handling of an store changes for
|
||||
// Identifiers this needs adapted - more forward safe would be
|
||||
// calling scan_expression directly.
|
||||
self.register_name(id, SymbolUsage::Assigned, range.start)?;
|
||||
self.register_name(id, SymbolUsage::Assigned, *range)?;
|
||||
}
|
||||
} else {
|
||||
self.scan_expression(target, ExpressionContext::Store)?;
|
||||
@@ -1204,20 +1223,20 @@ impl SymbolTableBuilder {
|
||||
fn scan_comprehension(
|
||||
&mut self,
|
||||
scope_name: &str,
|
||||
elt1: &ast::located::Expr,
|
||||
elt2: Option<&ast::located::Expr>,
|
||||
generators: &[ast::located::Comprehension],
|
||||
location: SourceLocation,
|
||||
elt1: &Expr,
|
||||
elt2: Option<&Expr>,
|
||||
generators: &[Comprehension],
|
||||
range: TextRange,
|
||||
) -> SymbolTableResult {
|
||||
// Comprehensions are compiled as functions, so create a scope for them:
|
||||
self.enter_scope(
|
||||
scope_name,
|
||||
SymbolTableType::Comprehension,
|
||||
location.row.get(),
|
||||
self.line_index_start(range),
|
||||
);
|
||||
|
||||
// Register the passed argument to the generator function as the name ".0"
|
||||
self.register_name(".0", SymbolUsage::Parameter, location)?;
|
||||
self.register_name(".0", SymbolUsage::Parameter, range)?;
|
||||
|
||||
self.scan_expression(elt1, ExpressionContext::Load)?;
|
||||
if let Some(elt2) = elt2 {
|
||||
@@ -1247,91 +1266,159 @@ impl SymbolTableBuilder {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_type_params(&mut self, type_params: &[ast::located::TypeParam]) -> SymbolTableResult {
|
||||
for type_param in type_params {
|
||||
fn scan_type_params(&mut self, type_params: &TypeParams) -> SymbolTableResult {
|
||||
for type_param in &type_params.type_params {
|
||||
match type_param {
|
||||
ast::located::TypeParam::TypeVar(ast::TypeParamTypeVar {
|
||||
TypeParam::TypeVar(TypeParamTypeVar {
|
||||
name,
|
||||
bound,
|
||||
range: type_var_range,
|
||||
..
|
||||
}) => {
|
||||
self.register_name(name.as_str(), SymbolUsage::Assigned, type_var_range.start)?;
|
||||
self.register_name(name.as_str(), SymbolUsage::Assigned, *type_var_range)?;
|
||||
if let Some(binding) = bound {
|
||||
self.scan_expression(binding, ExpressionContext::Load)?;
|
||||
}
|
||||
}
|
||||
ast::located::TypeParam::ParamSpec(ast::TypeParamParamSpec {
|
||||
TypeParam::ParamSpec(TypeParamParamSpec {
|
||||
name,
|
||||
range: param_spec_range,
|
||||
..
|
||||
}) => {
|
||||
self.register_name(name, SymbolUsage::Assigned, param_spec_range.start)?;
|
||||
self.register_name(name, SymbolUsage::Assigned, *param_spec_range)?;
|
||||
}
|
||||
ast::located::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple {
|
||||
TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
|
||||
name,
|
||||
range: type_var_tuple_range,
|
||||
..
|
||||
}) => {
|
||||
self.register_name(name, SymbolUsage::Assigned, type_var_tuple_range.start)?;
|
||||
self.register_name(name, SymbolUsage::Assigned, *type_var_tuple_range)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enter_function(
|
||||
fn scan_patterns(&mut self, patterns: &[Pattern]) -> SymbolTableResult {
|
||||
for pattern in patterns {
|
||||
self.scan_pattern(pattern)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_pattern(&mut self, pattern: &Pattern) -> SymbolTableResult {
|
||||
use Pattern::*;
|
||||
match pattern {
|
||||
MatchValue(PatternMatchValue { value, .. }) => {
|
||||
self.scan_expression(value, ExpressionContext::Load)?
|
||||
}
|
||||
MatchSingleton(_) => {}
|
||||
MatchSequence(PatternMatchSequence { patterns, .. }) => self.scan_patterns(patterns)?,
|
||||
MatchMapping(PatternMatchMapping {
|
||||
keys,
|
||||
patterns,
|
||||
rest,
|
||||
..
|
||||
}) => {
|
||||
self.scan_expressions(keys, ExpressionContext::Load)?;
|
||||
self.scan_patterns(patterns)?;
|
||||
if let Some(rest) = rest {
|
||||
self.register_ident(rest, SymbolUsage::Assigned)?;
|
||||
}
|
||||
}
|
||||
MatchClass(PatternMatchClass { cls, arguments, .. }) => {
|
||||
self.scan_expression(cls, ExpressionContext::Load)?;
|
||||
self.scan_patterns(&arguments.patterns)?;
|
||||
for kw in &arguments.keywords {
|
||||
self.scan_pattern(&kw.pattern)?;
|
||||
}
|
||||
}
|
||||
MatchStar(PatternMatchStar { name, .. }) => {
|
||||
if let Some(name) = name {
|
||||
self.register_ident(name, SymbolUsage::Assigned)?;
|
||||
}
|
||||
}
|
||||
MatchAs(PatternMatchAs { pattern, name, .. }) => {
|
||||
if let Some(pattern) = pattern {
|
||||
self.scan_pattern(pattern)?;
|
||||
}
|
||||
if let Some(name) = name {
|
||||
self.register_ident(name, SymbolUsage::Assigned)?;
|
||||
}
|
||||
}
|
||||
MatchOr(PatternMatchOr { patterns, .. }) => self.scan_patterns(patterns)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enter_scope_with_parameters(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &ast::located::Arguments,
|
||||
line_number: LineNumber,
|
||||
parameters: &Parameters,
|
||||
line_number: u32,
|
||||
) -> SymbolTableResult {
|
||||
// Evaluate eventual default parameters:
|
||||
for default in args
|
||||
for default in parameters
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.chain(args.args.iter())
|
||||
.chain(args.kwonlyargs.iter())
|
||||
.chain(parameters.args.iter())
|
||||
.chain(parameters.kwonlyargs.iter())
|
||||
.filter_map(|arg| arg.default.as_ref())
|
||||
{
|
||||
self.scan_expression(default, ExpressionContext::Load)?; // not ExprContext?
|
||||
}
|
||||
|
||||
// Annotations are scanned in outer scope:
|
||||
for annotation in args
|
||||
for annotation in parameters
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.chain(args.args.iter())
|
||||
.chain(args.kwonlyargs.iter())
|
||||
.filter_map(|arg| arg.def.annotation.as_ref())
|
||||
.chain(parameters.args.iter())
|
||||
.chain(parameters.kwonlyargs.iter())
|
||||
.filter_map(|arg| arg.parameter.annotation.as_ref())
|
||||
{
|
||||
self.scan_annotation(annotation)?;
|
||||
}
|
||||
if let Some(annotation) = args.vararg.as_ref().and_then(|arg| arg.annotation.as_ref()) {
|
||||
if let Some(annotation) = parameters
|
||||
.vararg
|
||||
.as_ref()
|
||||
.and_then(|arg| arg.annotation.as_ref())
|
||||
{
|
||||
self.scan_annotation(annotation)?;
|
||||
}
|
||||
if let Some(annotation) = args.kwarg.as_ref().and_then(|arg| arg.annotation.as_ref()) {
|
||||
if let Some(annotation) = parameters
|
||||
.kwarg
|
||||
.as_ref()
|
||||
.and_then(|arg| arg.annotation.as_ref())
|
||||
{
|
||||
self.scan_annotation(annotation)?;
|
||||
}
|
||||
|
||||
self.enter_scope(name, SymbolTableType::Function, line_number.get());
|
||||
self.enter_scope(name, SymbolTableType::Function, line_number);
|
||||
|
||||
// Fill scope with parameter names:
|
||||
self.scan_parameters(&args.posonlyargs)?;
|
||||
self.scan_parameters(&args.args)?;
|
||||
self.scan_parameters(&args.kwonlyargs)?;
|
||||
if let Some(name) = &args.vararg {
|
||||
self.scan_parameters(¶meters.posonlyargs)?;
|
||||
self.scan_parameters(¶meters.args)?;
|
||||
self.scan_parameters(¶meters.kwonlyargs)?;
|
||||
if let Some(name) = ¶meters.vararg {
|
||||
self.scan_parameter(name)?;
|
||||
}
|
||||
if let Some(name) = &args.kwarg {
|
||||
if let Some(name) = ¶meters.kwarg {
|
||||
self.scan_parameter(name)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_ident(&mut self, ident: &Identifier, role: SymbolUsage) -> SymbolTableResult {
|
||||
self.register_name(ident.as_str(), role, ident.range)
|
||||
}
|
||||
|
||||
fn register_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
role: SymbolUsage,
|
||||
location: SourceLocation,
|
||||
range: TextRange,
|
||||
) -> SymbolTableResult {
|
||||
let location = self.source_code.source_location(range.start());
|
||||
let location = Some(location);
|
||||
let scope_depth = self.tables.len();
|
||||
let table = self.tables.last_mut().unwrap();
|
||||
|
||||
@@ -9,7 +9,10 @@ repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
rustpython-parser-core = { workspace = true, features=["location"] }
|
||||
# rustpython-parser-core = { workspace = true, features=["location"] }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
ruff_source_file = { workspace = true }
|
||||
|
||||
bitflags = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
|
||||
@@ -5,11 +5,13 @@ use bitflags::bitflags;
|
||||
use itertools::Itertools;
|
||||
use malachite_bigint::BigInt;
|
||||
use num_complex::Complex64;
|
||||
use rustpython_parser_core::source_code::{OneIndexed, SourceLocation};
|
||||
pub use ruff_python_ast::ConversionFlag;
|
||||
// use rustpython_parser_core::source_code::{OneIndexed, SourceLocation};
|
||||
use ruff_source_file::{OneIndexed, SourceLocation};
|
||||
use std::marker::PhantomData;
|
||||
use std::{collections::BTreeSet, fmt, hash, mem};
|
||||
|
||||
pub use rustpython_parser_core::ConversionFlag;
|
||||
// pub use rustpython_parser_core::ConversionFlag;
|
||||
|
||||
pub trait Constant: Sized {
|
||||
type Name: AsRef<str>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::bytecode::*;
|
||||
use malachite_bigint::{BigInt, Sign};
|
||||
use num_complex::Complex64;
|
||||
use rustpython_parser_core::source_code::{OneIndexed, SourceLocation};
|
||||
use ruff_source_file::{OneIndexed, SourceLocation};
|
||||
use std::convert::Infallible;
|
||||
|
||||
pub const FORMAT_VERSION: u32 = 4;
|
||||
@@ -185,8 +185,8 @@ pub fn deserialize_code<R: Read, Bag: ConstantBag>(
|
||||
let locations = (0..len)
|
||||
.map(|_| {
|
||||
Ok(SourceLocation {
|
||||
row: OneIndexed::new(rdr.read_u32()?).ok_or(MarshalError::InvalidLocation)?,
|
||||
column: OneIndexed::from_zero_indexed(rdr.read_u32()?),
|
||||
row: OneIndexed::new(rdr.read_u32()? as _).ok_or(MarshalError::InvalidLocation)?,
|
||||
column: OneIndexed::from_zero_indexed(rdr.read_u32()? as _),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Box<[SourceLocation]>>>()?;
|
||||
@@ -200,7 +200,7 @@ pub fn deserialize_code<R: Read, Bag: ConstantBag>(
|
||||
let len = rdr.read_u32()?;
|
||||
let source_path = bag.make_name(rdr.read_str(len)?);
|
||||
|
||||
let first_line_number = OneIndexed::new(rdr.read_u32()?);
|
||||
let first_line_number = OneIndexed::new(rdr.read_u32()? as _);
|
||||
let max_stackdepth = rdr.read_u32()?;
|
||||
|
||||
let len = rdr.read_u32()?;
|
||||
@@ -589,8 +589,8 @@ pub fn serialize_code<W: Write, C: Constant>(buf: &mut W, code: &CodeObject<C>)
|
||||
|
||||
write_len(buf, code.locations.len());
|
||||
for loc in &*code.locations {
|
||||
buf.write_u32(loc.row.get());
|
||||
buf.write_u32(loc.column.to_zero_indexed());
|
||||
buf.write_u32(loc.row.get() as _);
|
||||
buf.write_u32(loc.column.to_zero_indexed() as _);
|
||||
}
|
||||
|
||||
buf.write_u16(code.flags.bits());
|
||||
@@ -601,7 +601,7 @@ pub fn serialize_code<W: Write, C: Constant>(buf: &mut W, code: &CodeObject<C>)
|
||||
|
||||
write_vec(buf, code.source_path.as_ref().as_bytes());
|
||||
|
||||
buf.write_u32(code.first_line_number.map_or(0, |x| x.get()));
|
||||
buf.write_u32(code.first_line_number.map_or(0, |x| x.get() as _));
|
||||
buf.write_u32(code.max_stackdepth);
|
||||
|
||||
write_vec(buf, code.obj_name.as_ref().as_bytes());
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub use rustpython_parser_core::mode::ModeParseError;
|
||||
pub use ruff_python_parser::ModeParseError;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Mode {
|
||||
@@ -22,12 +22,14 @@ impl std::str::FromStr for Mode {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mode> for rustpython_parser_core::Mode {
|
||||
impl From<Mode> for ruff_python_parser::Mode {
|
||||
fn from(mode: Mode) -> Self {
|
||||
match mode {
|
||||
Mode::Exec => Self::Module,
|
||||
Mode::Eval => Self::Expression,
|
||||
Mode::Single | Mode::BlockExpr => Self::Interactive,
|
||||
// TODO: Improve ruff API
|
||||
// ruff does not have an interactive mode
|
||||
Mode::Single | Mode::BlockExpr => Self::Ipython,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "rustpython-compiler-source"
|
||||
description = "RustPython Source and Index"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
ruff_source_file = { workspace = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,42 @@
|
||||
pub use ruff_source_file::{LineIndex, OneIndexed as LineNumber, SourceLocation};
|
||||
use ruff_text_size::TextRange;
|
||||
pub use ruff_text_size::TextSize;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SourceCode<'src> {
|
||||
pub path: &'src str,
|
||||
pub text: &'src str,
|
||||
pub index: LineIndex,
|
||||
}
|
||||
|
||||
impl<'src> SourceCode<'src> {
|
||||
pub fn new(path: &'src str, text: &'src str) -> Self {
|
||||
let index = LineIndex::from_source_text(text);
|
||||
Self { path, text, index }
|
||||
}
|
||||
|
||||
pub fn line_index(&self, offset: TextSize) -> LineNumber {
|
||||
self.index.line_index(offset)
|
||||
}
|
||||
|
||||
pub fn source_location(&self, offset: TextSize) -> SourceLocation {
|
||||
self.index.source_location(offset, self.text)
|
||||
}
|
||||
|
||||
pub fn get_range(&'src self, range: TextRange) -> &'src str {
|
||||
&self.text[range.start().to_usize()..range.end().to_usize()]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SourceCodeOwned {
|
||||
pub path: String,
|
||||
pub text: String,
|
||||
pub index: LineIndex,
|
||||
}
|
||||
|
||||
impl SourceCodeOwned {
|
||||
pub fn new(path: String, text: String) -> Self {
|
||||
let index = LineIndex::from_source_text(&text);
|
||||
Self { path, text, index }
|
||||
}
|
||||
}
|
||||
+261
-52
@@ -1,69 +1,128 @@
|
||||
use ruff_source_file::SourceLocation;
|
||||
use rustpython_codegen::{compile, symboltable};
|
||||
use rustpython_parser::ast::{self as ast, ConstantOptimizer, fold::Fold};
|
||||
|
||||
pub use rustpython_codegen::compile::CompileOpts;
|
||||
pub use rustpython_compiler_core::{Mode, bytecode::CodeObject};
|
||||
pub use rustpython_parser::{Parse, source_code::LinearLocator};
|
||||
use rustpython_compiler_source::SourceCode;
|
||||
|
||||
// these modules are out of repository. re-exporting them here for convenience.
|
||||
pub use ruff_python_ast as ast;
|
||||
pub use ruff_python_parser as parser;
|
||||
pub use rustpython_codegen as codegen;
|
||||
pub use rustpython_compiler_core as core;
|
||||
pub use rustpython_parser as parser;
|
||||
pub use rustpython_compiler_source as source;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CompileErrorType {
|
||||
Codegen(rustpython_codegen::error::CodegenErrorType),
|
||||
Parse(parser::ParseErrorType),
|
||||
#[error(transparent)]
|
||||
Codegen(#[from] codegen::error::CodegenErrorType),
|
||||
#[error(transparent)]
|
||||
Parse(#[from] parser::ParseErrorType),
|
||||
}
|
||||
|
||||
impl std::error::Error for CompileErrorType {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
CompileErrorType::Codegen(e) => e.source(),
|
||||
CompileErrorType::Parse(e) => e.source(),
|
||||
}
|
||||
}
|
||||
#[derive(Error, Debug)]
|
||||
pub struct ParseError {
|
||||
#[source]
|
||||
pub error: parser::ParseErrorType,
|
||||
pub location: SourceLocation,
|
||||
pub source_path: String,
|
||||
}
|
||||
impl std::fmt::Display for CompileErrorType {
|
||||
|
||||
impl std::fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
CompileErrorType::Codegen(e) => e.fmt(f),
|
||||
CompileErrorType::Parse(e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<rustpython_codegen::error::CodegenErrorType> for CompileErrorType {
|
||||
fn from(source: rustpython_codegen::error::CodegenErrorType) -> Self {
|
||||
CompileErrorType::Codegen(source)
|
||||
}
|
||||
}
|
||||
impl From<parser::ParseErrorType> for CompileErrorType {
|
||||
fn from(source: parser::ParseErrorType) -> Self {
|
||||
CompileErrorType::Parse(source)
|
||||
self.error.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CompileError = rustpython_parser::source_code::LocatedError<CompileErrorType>;
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
Codegen(#[from] codegen::error::CodegenError),
|
||||
#[error(transparent)]
|
||||
Parse(#[from] ParseError),
|
||||
}
|
||||
|
||||
impl CompileError {
|
||||
pub fn from_ruff_parse_error(error: parser::ParseError, source_code: &SourceCode<'_>) -> Self {
|
||||
let location = source_code.source_location(error.location.start());
|
||||
Self::Parse(ParseError {
|
||||
error: error.error,
|
||||
location,
|
||||
source_path: source_code.path.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn location(&self) -> Option<SourceLocation> {
|
||||
match self {
|
||||
CompileError::Codegen(codegen_error) => codegen_error.location.clone(),
|
||||
CompileError::Parse(parse_error) => Some(parse_error.location.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn python_location(&self) -> (usize, usize) {
|
||||
match self {
|
||||
CompileError::Codegen(codegen_error) => {
|
||||
if let Some(location) = &codegen_error.location {
|
||||
(location.row.get(), location.column.get())
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
CompileError::Parse(parse_error) => (
|
||||
parse_error.location.row.get(),
|
||||
parse_error.location.column.get(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_path(&self) -> &str {
|
||||
match self {
|
||||
CompileError::Codegen(codegen_error) => &codegen_error.source_path,
|
||||
CompileError::Parse(parse_error) => &parse_error.source_path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile a given source code into a bytecode object.
|
||||
pub fn compile(
|
||||
source: &str,
|
||||
mode: Mode,
|
||||
source_path: String,
|
||||
source_path: &str,
|
||||
opts: CompileOpts,
|
||||
) -> Result<CodeObject, CompileError> {
|
||||
let mut locator = LinearLocator::new(source);
|
||||
let mut ast = match parser::parse(source, mode.into(), &source_path) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Err(locator.locate_error(e)),
|
||||
};
|
||||
if opts.optimize > 0 {
|
||||
ast = ConstantOptimizer::new()
|
||||
.fold_mod(ast)
|
||||
.unwrap_or_else(|e| match e {});
|
||||
}
|
||||
let ast = locator.fold_mod(ast).unwrap_or_else(|e| match e {});
|
||||
compile::compile_top(&ast, source_path, mode, opts).map_err(|e| e.into())
|
||||
// TODO: do this less hackily; ruff's parser should translate a CRLF line
|
||||
// break in a multiline string into just an LF in the parsed value
|
||||
#[cfg(windows)]
|
||||
let source = &source.replace("\r\n", "\n");
|
||||
let source_code = SourceCode::new(source_path, source);
|
||||
_compile(source_code, mode, opts)
|
||||
// let index = LineIndex::from_source_text(source);
|
||||
// let source_code = SourceCode::new(source, &index);
|
||||
// let mut locator = LinearLocator::new(source);
|
||||
// let mut ast = match parser::parse(source, mode.into(), &source_path) {
|
||||
// Ok(x) => x,
|
||||
// Err(e) => return Err(locator.locate_error(e)),
|
||||
// };
|
||||
|
||||
// TODO:
|
||||
// if opts.optimize > 0 {
|
||||
// ast = ConstantOptimizer::new()
|
||||
// .fold_mod(ast)
|
||||
// .unwrap_or_else(|e| match e {});
|
||||
// }
|
||||
// let ast = locator.fold_mod(ast).unwrap_or_else(|e| match e {});
|
||||
}
|
||||
|
||||
fn _compile(
|
||||
source_code: SourceCode<'_>,
|
||||
mode: Mode,
|
||||
opts: CompileOpts,
|
||||
) -> Result<CodeObject, CompileError> {
|
||||
let parsed = parser::parse(source_code.text, mode.into())
|
||||
.map_err(|err| CompileError::from_ruff_parse_error(err, &source_code))?;
|
||||
let ast = parsed.into_syntax();
|
||||
compile::compile_top(ast, source_code, mode, opts).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn compile_symtable(
|
||||
@@ -71,20 +130,170 @@ pub fn compile_symtable(
|
||||
mode: Mode,
|
||||
source_path: &str,
|
||||
) -> Result<symboltable::SymbolTable, CompileError> {
|
||||
let mut locator = LinearLocator::new(source);
|
||||
let source_code = SourceCode::new(source_path, source);
|
||||
_compile_symtable(source_code, mode)
|
||||
}
|
||||
|
||||
pub fn _compile_symtable(
|
||||
source_code: SourceCode<'_>,
|
||||
mode: Mode,
|
||||
) -> Result<symboltable::SymbolTable, CompileError> {
|
||||
let res = match mode {
|
||||
Mode::Exec | Mode::Single | Mode::BlockExpr => {
|
||||
let ast =
|
||||
ast::Suite::parse(source, source_path).map_err(|e| locator.locate_error(e))?;
|
||||
let ast = locator.fold(ast).unwrap();
|
||||
symboltable::SymbolTable::scan_program(&ast)
|
||||
let ast = ruff_python_parser::parse_module(source_code.text)
|
||||
.map_err(|e| CompileError::from_ruff_parse_error(e, &source_code))?;
|
||||
symboltable::SymbolTable::scan_program(&ast.into_syntax(), source_code.clone())
|
||||
}
|
||||
Mode::Eval => {
|
||||
let expr =
|
||||
ast::Expr::parse(source, source_path).map_err(|e| locator.locate_error(e))?;
|
||||
let expr = locator.fold(expr).unwrap();
|
||||
symboltable::SymbolTable::scan_expr(&expr)
|
||||
let ast =
|
||||
ruff_python_parser::parse(source_code.text, ruff_python_parser::Mode::Expression)
|
||||
.map_err(|e| CompileError::from_ruff_parse_error(e, &source_code))?;
|
||||
symboltable::SymbolTable::scan_expr(
|
||||
&ast.into_syntax().expect_expression(),
|
||||
source_code.clone(),
|
||||
)
|
||||
}
|
||||
};
|
||||
res.map_err(|e| e.into_codegen_error(source_path.to_owned()).into())
|
||||
res.map_err(|e| e.into_codegen_error(source_code.path.to_owned()).into())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile() {
|
||||
let code = "x = 'abc'";
|
||||
let compiled = compile(&code, Mode::Single, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_phello() {
|
||||
let code = r#"
|
||||
initialized = True
|
||||
def main():
|
||||
print("Hello world!")
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
"#;
|
||||
let compiled = compile(&code, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_if_elif_else() {
|
||||
let code = r#"
|
||||
if False:
|
||||
pass
|
||||
elif False:
|
||||
pass
|
||||
elif False:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
"#;
|
||||
let compiled = compile(&code, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_lambda() {
|
||||
let code = r#"
|
||||
lambda: 'a'
|
||||
"#;
|
||||
let compiled = compile(&code, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_lambda2() {
|
||||
let code = r#"
|
||||
(lambda x: f'hello, {x}')('world}')
|
||||
"#;
|
||||
let compiled = compile(&code, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_lambda3() {
|
||||
let code = r#"
|
||||
def g():
|
||||
pass
|
||||
def f():
|
||||
if False:
|
||||
return lambda x: g(x)
|
||||
elif False:
|
||||
return g
|
||||
else:
|
||||
return g
|
||||
"#;
|
||||
let compiled = compile(&code, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_int() {
|
||||
let code = r#"
|
||||
a = 0xFF
|
||||
"#;
|
||||
let compiled = compile(&code, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_bigint() {
|
||||
let code = r#"
|
||||
a = 0xFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
"#;
|
||||
let compiled = compile(&code, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_fstring() {
|
||||
let code1 = r#"
|
||||
assert f"1" == '1'
|
||||
"#;
|
||||
let compiled = compile(&code1, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
|
||||
let code2 = r#"
|
||||
assert f"{1}" == '1'
|
||||
"#;
|
||||
let compiled = compile(&code2, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
let code3 = r#"
|
||||
assert f"{1+1}" == '2'
|
||||
"#;
|
||||
let compiled = compile(&code3, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
|
||||
let code4 = r#"
|
||||
assert f"{{{(lambda: f'{1}')}" == '{1'
|
||||
"#;
|
||||
let compiled = compile(&code4, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
|
||||
let code5 = r#"
|
||||
assert f"a{1}" == 'a1'
|
||||
"#;
|
||||
let compiled = compile(&code5, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
|
||||
let code6 = r#"
|
||||
assert f"{{{(lambda x: f'hello, {x}')('world}')}" == '{hello, world}'
|
||||
"#;
|
||||
let compiled = compile(&code6, Mode::Exec, "<>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_enum() {
|
||||
let code = r#"
|
||||
import enum
|
||||
@enum._simple_enum(enum.IntFlag, boundary=enum.KEEP)
|
||||
class RegexFlag:
|
||||
NOFLAG = 0
|
||||
DEBUG = 1
|
||||
print(RegexFlag.NOFLAG & RegexFlag.DEBUG)
|
||||
"#;
|
||||
let compiled = compile(&code, Mode::Exec, "<string>", CompileOpts::default());
|
||||
dbg!(compiled.expect("compile error"));
|
||||
}
|
||||
@@ -10,7 +10,7 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
rustpython-compiler-core = { workspace = true }
|
||||
rustpython-parser-core = { workspace = true }
|
||||
# rustpython-parser-core = { workspace = true }
|
||||
rustpython-doc = { workspace = true }
|
||||
|
||||
itertools = { workspace = true }
|
||||
|
||||
+1
-1
@@ -246,7 +246,7 @@ impl derive_impl::Compiler for Compiler {
|
||||
module_name: String,
|
||||
) -> Result<rustpython_compiler::CodeObject, Box<dyn std::error::Error>> {
|
||||
use rustpython_compiler::{CompileOpts, compile};
|
||||
Ok(compile(source, mode, module_name, CompileOpts::default())?)
|
||||
Ok(compile(source, mode, &module_name, CompileOpts::default())?)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ fn display_script(
|
||||
expand_code_objects: bool,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let source = fs::read_to_string(path)?;
|
||||
let code = compiler::compile(&source, mode, path.to_string_lossy().into_owned(), opts)?;
|
||||
let code = compiler::compile(&source, mode, &path.to_string_lossy(), opts)?;
|
||||
println!("{}:", path.display());
|
||||
if expand_code_objects {
|
||||
println!("{}", code.display_expand_code_objects());
|
||||
|
||||
@@ -8,7 +8,8 @@ extern crate env_logger;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use rustpython_parser::{Parse, ast};
|
||||
use ruff_python_parser::parse_module;
|
||||
use rustpython_compiler::ast;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
time::{Duration, Instant},
|
||||
@@ -68,18 +69,19 @@ fn parse_python_file(filename: &Path) -> ParsedFile {
|
||||
info!("Parsing file {:?}", filename);
|
||||
match std::fs::read_to_string(filename) {
|
||||
Err(e) => ParsedFile {
|
||||
// filename: Box::new(filename.to_path_buf()),
|
||||
// code: "".to_owned(),
|
||||
filename: Box::new(filename.to_path_buf()),
|
||||
code: "".to_owned(),
|
||||
num_lines: 0,
|
||||
result: Err(e.to_string()),
|
||||
},
|
||||
Ok(source) => {
|
||||
let num_lines = source.lines().count();
|
||||
let result =
|
||||
ast::Suite::parse(&source, &filename.to_string_lossy()).map_err(|e| e.to_string());
|
||||
let result = parse_module(&source)
|
||||
.map(|x| x.into_suite())
|
||||
.map_err(|e| e.to_string());
|
||||
ParsedFile {
|
||||
// filename: Box::new(filename.to_path_buf()),
|
||||
// code: source.to_string(),
|
||||
filename: Box::new(filename.to_path_buf()),
|
||||
code: source.to_string(),
|
||||
num_lines,
|
||||
result,
|
||||
}
|
||||
@@ -132,8 +134,8 @@ struct ScanResult {
|
||||
}
|
||||
|
||||
struct ParsedFile {
|
||||
// filename: Box<PathBuf>,
|
||||
// code: String,
|
||||
filename: Box<PathBuf>,
|
||||
code: String,
|
||||
num_lines: usize,
|
||||
result: ParseResult,
|
||||
}
|
||||
|
||||
+12
-14
@@ -1,10 +1,12 @@
|
||||
mod helper;
|
||||
|
||||
use rustpython_parser::{ParseErrorType, Tok, lexer::LexicalErrorType};
|
||||
use rustpython_compiler::{
|
||||
CompileError, ParseError, parser::ParseErrorType, parser::lexer::LexicalErrorType,
|
||||
};
|
||||
use rustpython_vm::{
|
||||
AsObject, PyResult, VirtualMachine,
|
||||
builtins::PyBaseExceptionRef,
|
||||
compiler::{self, CompileError, CompileErrorType},
|
||||
compiler::{self},
|
||||
readline::{Readline, ReadlineResult},
|
||||
scope::Scope,
|
||||
};
|
||||
@@ -35,29 +37,25 @@ fn shell_exec(
|
||||
ShellExecResult::Ok
|
||||
}
|
||||
}
|
||||
Err(CompileError {
|
||||
error: CompileErrorType::Parse(ParseErrorType::Lexical(LexicalErrorType::Eof)),
|
||||
Err(CompileError::Parse(ParseError {
|
||||
error: ParseErrorType::Lexical(LexicalErrorType::Eof),
|
||||
..
|
||||
})
|
||||
| Err(CompileError {
|
||||
error: CompileErrorType::Parse(ParseErrorType::Eof),
|
||||
..
|
||||
}) => ShellExecResult::Continue,
|
||||
})) => ShellExecResult::Continue,
|
||||
Err(err) => {
|
||||
// bad_error == true if we are handling an error that should be thrown even if we are continuing
|
||||
// if its an indentation error, set to true if we are continuing and the error is on column 0,
|
||||
// since indentations errors on columns other than 0 should be ignored.
|
||||
// if its an unrecognized token for dedent, set to false
|
||||
|
||||
let bad_error = match err.error {
|
||||
CompileErrorType::Parse(ref p) => {
|
||||
let bad_error = match err {
|
||||
CompileError::Parse(ref p) => {
|
||||
if matches!(
|
||||
p,
|
||||
p.error,
|
||||
ParseErrorType::Lexical(LexicalErrorType::IndentationError)
|
||||
) {
|
||||
continuing && err.location.is_some()
|
||||
continuing // && p.location.is_some()
|
||||
} else {
|
||||
!matches!(p, ParseErrorType::UnrecognizedToken(Tok::Dedent, _))
|
||||
true // !matches!(p, ParseErrorType::UnrecognizedToken(Tok::Dedent, _))
|
||||
}
|
||||
}
|
||||
_ => true, // It is a bad error for everything else
|
||||
|
||||
@@ -10,7 +10,7 @@ mod decl {
|
||||
stderr,
|
||||
" File \"{}\", line {} in {}",
|
||||
frame.code.source_path,
|
||||
frame.current_location().row.to_usize(),
|
||||
frame.current_location().row,
|
||||
frame.code.obj_name
|
||||
)
|
||||
}
|
||||
|
||||
+13
-8
@@ -19,9 +19,9 @@ freeze-stdlib = ["encodings"]
|
||||
jit = ["rustpython-jit"]
|
||||
threading = ["rustpython-common/threading"]
|
||||
compiler = ["parser", "codegen", "rustpython-compiler"]
|
||||
ast = ["rustpython-ast"]
|
||||
ast = ["ruff_python_ast", "ruff_text_size", "ruff_source_file"]
|
||||
codegen = ["rustpython-codegen", "ast"]
|
||||
parser = ["rustpython-parser", "ast"]
|
||||
parser = ["ast"]
|
||||
serde = ["dep:serde"]
|
||||
wasmbind = ["chrono/wasmbind", "getrandom/wasm_js", "wasm-bindgen"]
|
||||
|
||||
@@ -32,10 +32,15 @@ rustpython-common = { workspace = true }
|
||||
rustpython-derive = { workspace = true }
|
||||
rustpython-jit = { workspace = true, optional = true }
|
||||
|
||||
rustpython-ast = { workspace = true, optional = true }
|
||||
rustpython-parser = { workspace = true, optional = true }
|
||||
ruff_python_ast = { workspace = true, optional = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
ruff_text_size = { workspace = true, optional = true }
|
||||
ruff_source_file = { workspace = true, optional = true }
|
||||
# rustpython-ast = { workspace = true, optional = true }
|
||||
# rustpython-parser = { workspace = true, optional = true }
|
||||
rustpython-compiler-core = { workspace = true }
|
||||
rustpython-parser-core = { workspace = true }
|
||||
rustpython-compiler-source = { workspace = true }
|
||||
# rustpython-parser-core = { workspace = true }
|
||||
rustpython-literal = { workspace = true }
|
||||
rustpython-format = { workspace = true }
|
||||
rustpython-sre_engine = { workspace = true }
|
||||
@@ -87,9 +92,9 @@ unicode_names2 = { workspace = true }
|
||||
# https://github.com/RustPython/RustPython/pull/832#discussion_r275428939
|
||||
unicode-casing = "0.1.0"
|
||||
# update version all at the same time
|
||||
unic-ucd-bidi = "0.9.0"
|
||||
unic-ucd-category = "0.9.0"
|
||||
unic-ucd-ident = "0.9.0"
|
||||
unic-ucd-bidi = "0.9.0"
|
||||
unic-ucd-category = "0.9.0"
|
||||
unic-ucd-ident = "0.9.0"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
rustix = { workspace = true }
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
convert::ToPyObject,
|
||||
frozen,
|
||||
function::{FuncArgs, OptionalArg},
|
||||
source_code::OneIndexed,
|
||||
source::LineNumber,
|
||||
types::Representable,
|
||||
};
|
||||
use malachite_bigint::BigInt;
|
||||
@@ -279,7 +279,7 @@ impl PyCode {
|
||||
|
||||
#[pygetset]
|
||||
fn co_firstlineno(&self) -> u32 {
|
||||
self.code.first_line_number.map_or(0, |n| n.get())
|
||||
self.code.first_line_number.map_or(0, |n| n.get() as _)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
@@ -351,7 +351,7 @@ impl PyCode {
|
||||
};
|
||||
|
||||
let first_line_number = match args.co_firstlineno {
|
||||
OptionalArg::Present(first_line_number) => OneIndexed::new(first_line_number),
|
||||
OptionalArg::Present(first_line_number) => LineNumber::new(first_line_number as _),
|
||||
OptionalArg::Missing => self.code.first_line_number,
|
||||
};
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ impl Frame {
|
||||
|
||||
#[pygetset]
|
||||
pub fn f_lineno(&self) -> usize {
|
||||
self.current_location().row.to_usize()
|
||||
self.current_location().row.get()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
|
||||
@@ -2,7 +2,7 @@ use rustpython_common::lock::PyMutex;
|
||||
|
||||
use super::PyType;
|
||||
use crate::{
|
||||
Context, Py, PyPayload, PyRef, class::PyClassImpl, frame::FrameRef, source_code::LineNumber,
|
||||
Context, Py, PyPayload, PyRef, class::PyClassImpl, frame::FrameRef, source::LineNumber,
|
||||
};
|
||||
|
||||
#[pyclass(module = false, name = "traceback", traverse)]
|
||||
@@ -47,7 +47,7 @@ impl PyTraceback {
|
||||
|
||||
#[pygetset]
|
||||
fn tb_lineno(&self) -> usize {
|
||||
self.lineno.to_usize()
|
||||
self.lineno.get()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
|
||||
+27
-15
@@ -1,37 +1,49 @@
|
||||
#[cfg(feature = "rustpython-codegen")]
|
||||
#[cfg(feature = "codegen")]
|
||||
pub use rustpython_codegen::CompileOpts;
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
#[cfg(feature = "compiler")]
|
||||
pub use rustpython_compiler::*;
|
||||
#[cfg(not(feature = "rustpython-compiler"))]
|
||||
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
pub use rustpython_compiler_source as source;
|
||||
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
pub use rustpython_compiler_core::Mode;
|
||||
|
||||
#[cfg(not(feature = "rustpython-compiler"))]
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
pub use rustpython_compiler_core as core;
|
||||
|
||||
#[cfg(not(feature = "rustpython-compiler"))]
|
||||
pub use rustpython_parser_core as parser;
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
pub use ruff_python_parser as parser;
|
||||
|
||||
#[cfg(not(feature = "rustpython-compiler"))]
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
mod error {
|
||||
#[cfg(all(feature = "rustpython-parser", feature = "rustpython-codegen"))]
|
||||
#[cfg(all(feature = "parser", feature = "codegen"))]
|
||||
panic!("Use --features=compiler to enable both parser and codegen");
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CompileErrorType {
|
||||
#[cfg(feature = "rustpython-codegen")]
|
||||
#[cfg(feature = "codegen")]
|
||||
#[error(transparent)]
|
||||
Codegen(#[from] rustpython_codegen::error::CodegenErrorType),
|
||||
#[cfg(feature = "rustpython-parser")]
|
||||
Codegen(#[from] super::codegen::error::CodegenErrorType),
|
||||
#[cfg(feature = "parser")]
|
||||
#[error(transparent)]
|
||||
Parse(#[from] rustpython_parser::error::ParseErrorType),
|
||||
Parse(#[from] super::parser::ParseErrorType),
|
||||
}
|
||||
|
||||
pub type CompileError = rustpython_parser_core::source_code::LocatedError<CompileErrorType>;
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CompileError {
|
||||
#[cfg(feature = "codegen")]
|
||||
#[error(transparent)]
|
||||
Codegen(#[from] super::codegen::error::CodegenError),
|
||||
#[cfg(feature = "parser")]
|
||||
#[error(transparent)]
|
||||
Parse(#[from] super::parser::ParseError),
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "rustpython-compiler"))]
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
pub use error::{CompileError, CompileErrorType};
|
||||
|
||||
#[cfg(any(feature = "rustpython-parser", feature = "rustpython-codegen"))]
|
||||
#[cfg(any(feature = "parser", feature = "codegen"))]
|
||||
impl crate::convert::ToPyException for (CompileError, Option<&str>) {
|
||||
fn to_pyexception(&self, vm: &crate::VirtualMachine) -> crate::builtins::PyBaseExceptionRef {
|
||||
vm.new_syntax_error(&self.0, self.1)
|
||||
|
||||
@@ -378,7 +378,7 @@ fn write_traceback_entry<W: Write>(
|
||||
tb_entry.lineno,
|
||||
tb_entry.frame.code.obj_name
|
||||
)?;
|
||||
print_source_line(output, filename, tb_entry.lineno.to_usize())?;
|
||||
print_source_line(output, filename, tb_entry.lineno.get())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@ use crate::{
|
||||
function::{ArgMapping, Either, FuncArgs},
|
||||
protocol::{PyIter, PyIterReturn},
|
||||
scope::Scope,
|
||||
source_code::SourceLocation,
|
||||
source::SourceLocation,
|
||||
stdlib::{builtins, typing::_typing},
|
||||
vm::{Context, PyMethod},
|
||||
};
|
||||
@@ -166,7 +166,7 @@ impl Frame {
|
||||
}
|
||||
|
||||
pub fn current_location(&self) -> SourceLocation {
|
||||
self.code.locations[self.lasti() as usize - 1]
|
||||
self.code.locations[self.lasti() as usize - 1].clone()
|
||||
}
|
||||
|
||||
pub fn lasti(&self) -> u32 {
|
||||
@@ -380,7 +380,7 @@ impl ExecutingFrame<'_> {
|
||||
// 2. Add new entry with current execution position (filename, lineno, code_object) to traceback.
|
||||
// 3. Unwind block stack till appropriate handler is found.
|
||||
|
||||
let loc = frame.code.locations[idx];
|
||||
let loc = frame.code.locations[idx].clone();
|
||||
let next = exception.traceback();
|
||||
let new_traceback =
|
||||
PyTraceback::new(next, frame.object.to_owned(), frame.lasti(), loc.row);
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ pub mod warn;
|
||||
#[cfg(windows)]
|
||||
pub mod windows;
|
||||
|
||||
pub use self::compiler::parser::source_code;
|
||||
pub use self::compiler::source;
|
||||
pub use self::convert::{TryFromBorrowedObject, TryFromObject};
|
||||
pub use self::object::{
|
||||
AsObject, Py, PyAtomicRef, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact,
|
||||
|
||||
+211
-278
@@ -3,81 +3,55 @@
|
||||
//! This module makes use of the parser logic, and translates all ast nodes
|
||||
//! into python ast.AST objects.
|
||||
|
||||
mod r#gen;
|
||||
mod pyast;
|
||||
|
||||
use crate::builtins::{PyInt, PyStr};
|
||||
use crate::stdlib::ast::module::{Mod, ModInteractive};
|
||||
use crate::stdlib::ast::node::BoxedSlice;
|
||||
use crate::stdlib::ast::python::_ast;
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
||||
VirtualMachine,
|
||||
builtins::{self, PyDict, PyModule, PyStrRef, PyType},
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
|
||||
TryFromObject, VirtualMachine,
|
||||
builtins::PyIntRef,
|
||||
builtins::{PyDict, PyModule, PyStrRef, PyType},
|
||||
class::{PyClassImpl, StaticType},
|
||||
compiler::CompileError,
|
||||
compiler::core::bytecode::OpArgType,
|
||||
convert::ToPyException,
|
||||
source_code::{LinearLocator, OneIndexed, SourceLocation, SourceRange},
|
||||
compiler::{CompileError, ParseError},
|
||||
convert::ToPyObject,
|
||||
source::SourceCode,
|
||||
source::SourceLocation,
|
||||
};
|
||||
use num_complex::Complex64;
|
||||
use num_traits::{ToPrimitive, Zero};
|
||||
use rustpython_ast::{self as ast, fold::Fold};
|
||||
#[cfg(feature = "rustpython-codegen")]
|
||||
use node::Node;
|
||||
use ruff_python_ast as ruff;
|
||||
use ruff_source_file::OneIndexed;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
use rustpython_compiler_source::SourceCodeOwned;
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
use ruff_python_parser as parser;
|
||||
#[cfg(feature = "codegen")]
|
||||
use rustpython_codegen as codegen;
|
||||
#[cfg(feature = "rustpython-parser")]
|
||||
use rustpython_parser as parser;
|
||||
|
||||
#[pymodule]
|
||||
mod _ast {
|
||||
use crate::{
|
||||
AsObject, Context, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{PyStrRef, PyTupleRef},
|
||||
function::FuncArgs,
|
||||
};
|
||||
#[pyattr]
|
||||
#[pyclass(module = "_ast", name = "AST")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(crate) struct NodeAst;
|
||||
pub(crate) use python::_ast::NodeAst;
|
||||
|
||||
#[pyclass(flags(BASETYPE, HAS_DICT))]
|
||||
impl NodeAst {
|
||||
#[pyslot]
|
||||
#[pymethod(magic)]
|
||||
fn init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let fields = zelf.get_attr("_fields", vm)?;
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
let numargs = args.args.len();
|
||||
if numargs > fields.len() {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{} constructor takes at most {} positional argument{}",
|
||||
zelf.class().name(),
|
||||
fields.len(),
|
||||
if fields.len() == 1 { "" } else { "s" },
|
||||
)));
|
||||
}
|
||||
for (name, arg) in fields.iter().zip(args.args) {
|
||||
zelf.set_attr(name, arg, vm)?;
|
||||
}
|
||||
for (key, value) in args.kwargs {
|
||||
if let Some(pos) = fields.iter().position(|f| f.as_str() == key) {
|
||||
if pos < numargs {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{} got multiple values for argument '{}'",
|
||||
zelf.class().name(),
|
||||
key
|
||||
)));
|
||||
}
|
||||
}
|
||||
zelf.set_attr(vm.ctx.intern_str(key), value, vm)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
mod python;
|
||||
|
||||
#[pyattr(name = "_fields")]
|
||||
fn fields(ctx: &Context) -> PyTupleRef {
|
||||
ctx.empty_tuple.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr(name = "PyCF_ONLY_AST")]
|
||||
use super::PY_COMPILE_FLAG_AST_ONLY;
|
||||
}
|
||||
mod argument;
|
||||
mod basic;
|
||||
mod constant;
|
||||
mod elif_else_clause;
|
||||
mod exception;
|
||||
mod expression;
|
||||
mod module;
|
||||
mod node;
|
||||
mod operator;
|
||||
mod other;
|
||||
mod parameter;
|
||||
mod pattern;
|
||||
mod statement;
|
||||
mod string;
|
||||
mod type_ignore;
|
||||
mod type_parameters;
|
||||
|
||||
fn get_node_field(vm: &VirtualMachine, obj: &PyObject, field: &'static str, typ: &str) -> PyResult {
|
||||
vm.get_attribute_opt(obj.to_owned(), field)?
|
||||
@@ -94,248 +68,197 @@ fn get_node_field_opt(
|
||||
.filter(|obj| !vm.is_none(obj)))
|
||||
}
|
||||
|
||||
trait Node: Sized {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef;
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self>;
|
||||
fn get_int_field(
|
||||
vm: &VirtualMachine,
|
||||
obj: &PyObject,
|
||||
field: &'static str,
|
||||
typ: &str,
|
||||
) -> PyResult<PyRefExact<PyInt>> {
|
||||
get_node_field(vm, obj, field, typ)?
|
||||
.downcast_exact(vm)
|
||||
.map_err(|_| vm.new_type_error(format!("field \"{field}\" must have integer type")))
|
||||
}
|
||||
|
||||
impl<T: Node> Node for Vec<T> {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx
|
||||
.new_list(
|
||||
self.into_iter()
|
||||
.map(|node| node.ast_to_object(vm))
|
||||
.collect(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
vm.extract_elements_with(&object, |obj| Node::ast_from_object(vm, obj))
|
||||
}
|
||||
struct PySourceRange {
|
||||
start: PySourceLocation,
|
||||
end: PySourceLocation,
|
||||
}
|
||||
|
||||
impl<T: Node> Node for Box<T> {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
(*self).ast_to_object(vm)
|
||||
}
|
||||
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
T::ast_from_object(vm, object).map(Box::new)
|
||||
}
|
||||
pub struct PySourceLocation {
|
||||
row: Row,
|
||||
column: Column,
|
||||
}
|
||||
|
||||
impl<T: Node> Node for Option<T> {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
match self {
|
||||
Some(node) => node.ast_to_object(vm),
|
||||
None => vm.ctx.none(),
|
||||
impl PySourceLocation {
|
||||
fn to_source_location(&self) -> SourceLocation {
|
||||
SourceLocation {
|
||||
row: self.row.get_one_indexed(),
|
||||
column: self.column.get_one_indexed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
if vm.is_none(&object) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(T::ast_from_object(vm, object)?))
|
||||
}
|
||||
/// A one-based index into the lines.
|
||||
#[derive(Clone, Copy)]
|
||||
struct Row(OneIndexed);
|
||||
|
||||
impl Row {
|
||||
fn get(self) -> usize {
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
fn get_one_indexed(self) -> OneIndexed {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An UTF-8 index into the line.
|
||||
#[derive(Clone, Copy)]
|
||||
struct Column(TextSize);
|
||||
|
||||
impl Column {
|
||||
fn get(self) -> usize {
|
||||
self.0.to_usize()
|
||||
}
|
||||
|
||||
fn get_one_indexed(self) -> OneIndexed {
|
||||
OneIndexed::from_zero_indexed(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
fn text_range_to_source_range(
|
||||
source_code: &SourceCodeOwned,
|
||||
text_range: TextRange,
|
||||
) -> PySourceRange {
|
||||
let index = &source_code.index;
|
||||
let source = &source_code.text;
|
||||
|
||||
if source.is_empty() {
|
||||
return PySourceRange {
|
||||
start: PySourceLocation {
|
||||
row: Row(OneIndexed::from_zero_indexed(0)),
|
||||
column: Column(TextSize::new(0)),
|
||||
},
|
||||
end: PySourceLocation {
|
||||
row: Row(OneIndexed::from_zero_indexed(0)),
|
||||
column: Column(TextSize::new(0)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let start_row = index.line_index(text_range.start());
|
||||
let end_row = index.line_index(text_range.end());
|
||||
let start_col = text_range.start() - index.line_start(start_row, source);
|
||||
let end_col = text_range.end() - index.line_start(end_row, source);
|
||||
|
||||
PySourceRange {
|
||||
start: PySourceLocation {
|
||||
row: Row(start_row),
|
||||
column: Column(start_col),
|
||||
},
|
||||
end: PySourceLocation {
|
||||
row: Row(end_row),
|
||||
column: Column(end_col),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn range_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
name: &str,
|
||||
) -> PyResult<SourceRange> {
|
||||
fn make_location(row: u32, column: u32) -> Option<SourceLocation> {
|
||||
Some(SourceLocation {
|
||||
row: OneIndexed::new(row)?,
|
||||
column: OneIndexed::from_zero_indexed(column),
|
||||
})
|
||||
}
|
||||
let row = ast::Int::ast_from_object(vm, get_node_field(vm, &object, "lineno", name)?)?;
|
||||
let column = ast::Int::ast_from_object(vm, get_node_field(vm, &object, "col_offset", name)?)?;
|
||||
let location = make_location(row.to_u32(), column.to_u32());
|
||||
let end_row = get_node_field_opt(vm, &object, "end_lineno")?
|
||||
.map(|obj| ast::Int::ast_from_object(vm, obj))
|
||||
.transpose()?;
|
||||
let end_column = get_node_field_opt(vm, &object, "end_col_offset")?
|
||||
.map(|obj| ast::Int::ast_from_object(vm, obj))
|
||||
.transpose()?;
|
||||
let end_location = if let (Some(row), Some(column)) = (end_row, end_column) {
|
||||
make_location(row.to_u32(), column.to_u32())
|
||||
} else {
|
||||
None
|
||||
) -> PyResult<TextRange> {
|
||||
let start_row = get_int_field(vm, &object, "lineno", name)?;
|
||||
let start_column = get_int_field(vm, &object, "col_offset", name)?;
|
||||
let end_row = get_int_field(vm, &object, "end_lineno", name)?;
|
||||
let end_column = get_int_field(vm, &object, "end_col_offset", name)?;
|
||||
|
||||
let location = PySourceRange {
|
||||
start: PySourceLocation {
|
||||
row: Row(OneIndexed::new(start_row.try_to_primitive(vm)?).unwrap()),
|
||||
column: Column(TextSize::new(start_column.try_to_primitive(vm)?)),
|
||||
},
|
||||
end: PySourceLocation {
|
||||
row: Row(OneIndexed::new(end_row.try_to_primitive(vm)?).unwrap()),
|
||||
column: Column(TextSize::new(end_column.try_to_primitive(vm)?)),
|
||||
},
|
||||
};
|
||||
let range = SourceRange {
|
||||
start: location.unwrap_or_default(),
|
||||
end: end_location,
|
||||
};
|
||||
Ok(range)
|
||||
|
||||
Ok(source_range_to_text_range(source_code, location))
|
||||
}
|
||||
|
||||
fn node_add_location(dict: &Py<PyDict>, range: SourceRange, vm: &VirtualMachine) {
|
||||
fn source_range_to_text_range(source_code: &SourceCodeOwned, location: PySourceRange) -> TextRange {
|
||||
let source = &source_code.text;
|
||||
let index = &source_code.index;
|
||||
|
||||
if source.is_empty() {
|
||||
return TextRange::new(TextSize::new(0), TextSize::new(0));
|
||||
}
|
||||
|
||||
let start = index.offset(
|
||||
location.start.row.get_one_indexed(),
|
||||
location.start.column.get_one_indexed(),
|
||||
source,
|
||||
);
|
||||
let end = index.offset(
|
||||
location.end.row.get_one_indexed(),
|
||||
location.end.column.get_one_indexed(),
|
||||
source,
|
||||
);
|
||||
|
||||
TextRange::new(start, end)
|
||||
}
|
||||
|
||||
fn node_add_location(
|
||||
dict: &Py<PyDict>,
|
||||
range: TextRange,
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
) {
|
||||
let range = text_range_to_source_range(source_code, range);
|
||||
dict.set_item("lineno", vm.ctx.new_int(range.start.row.get()).into(), vm)
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"col_offset",
|
||||
vm.ctx.new_int(range.start.column.to_zero_indexed()).into(),
|
||||
vm.ctx.new_int(range.start.column.get()).into(),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
if let Some(end_location) = range.end {
|
||||
dict.set_item(
|
||||
"end_lineno",
|
||||
vm.ctx.new_int(end_location.row.get()).into(),
|
||||
vm,
|
||||
)
|
||||
dict.set_item("end_lineno", vm.ctx.new_int(range.end.row.get()).into(), vm)
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"end_col_offset",
|
||||
vm.ctx.new_int(end_location.column.to_zero_indexed()).into(),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
};
|
||||
dict.set_item(
|
||||
"end_col_offset",
|
||||
vm.ctx.new_int(range.end.column.get()).into(),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
impl Node for ast::String {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx.new_str(self).into()
|
||||
}
|
||||
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
let py_str = PyStrRef::try_from_object(vm, object)?;
|
||||
Ok(py_str.as_str().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for ast::Identifier {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let id: String = self.into();
|
||||
vm.ctx.new_str(id).into()
|
||||
}
|
||||
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
let py_str = PyStrRef::try_from_object(vm, object)?;
|
||||
Ok(ast::Identifier::new(py_str.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for ast::Int {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx.new_int(self.to_u32()).into()
|
||||
}
|
||||
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
let value = object.try_into_value(vm)?;
|
||||
Ok(ast::Int::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for bool {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx.new_int(self as u8).into()
|
||||
}
|
||||
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
i32::try_from_object(vm, object).map(|i| i != 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for ast::Constant {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
match self {
|
||||
ast::Constant::None => vm.ctx.none(),
|
||||
ast::Constant::Bool(b) => vm.ctx.new_bool(b).into(),
|
||||
ast::Constant::Str(s) => vm.ctx.new_str(s).into(),
|
||||
ast::Constant::Bytes(b) => vm.ctx.new_bytes(b).into(),
|
||||
ast::Constant::Int(i) => vm.ctx.new_int(i).into(),
|
||||
ast::Constant::Tuple(t) => vm
|
||||
.ctx
|
||||
.new_tuple(t.into_iter().map(|c| c.ast_to_object(vm)).collect())
|
||||
.into(),
|
||||
ast::Constant::Float(f) => vm.ctx.new_float(f).into(),
|
||||
ast::Constant::Complex { real, imag } => vm.new_pyobj(Complex64::new(real, imag)),
|
||||
ast::Constant::Ellipsis => vm.ctx.ellipsis(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
let constant = match_class!(match object {
|
||||
ref i @ builtins::int::PyInt => {
|
||||
let value = i.as_bigint();
|
||||
if object.class().is(vm.ctx.types.bool_type) {
|
||||
ast::Constant::Bool(!value.is_zero())
|
||||
} else {
|
||||
ast::Constant::Int(value.clone())
|
||||
}
|
||||
}
|
||||
ref f @ builtins::float::PyFloat => ast::Constant::Float(f.to_f64()),
|
||||
ref c @ builtins::complex::PyComplex => {
|
||||
let c = c.to_complex();
|
||||
ast::Constant::Complex {
|
||||
real: c.re,
|
||||
imag: c.im,
|
||||
}
|
||||
}
|
||||
ref s @ builtins::pystr::PyStr => ast::Constant::Str(s.as_str().to_owned()),
|
||||
ref b @ builtins::bytes::PyBytes => ast::Constant::Bytes(b.as_bytes().to_owned()),
|
||||
ref t @ builtins::tuple::PyTuple => {
|
||||
ast::Constant::Tuple(
|
||||
t.iter()
|
||||
.map(|elt| Self::ast_from_object(vm, elt.clone()))
|
||||
.collect::<Result<_, _>>()?,
|
||||
)
|
||||
}
|
||||
builtins::singletons::PyNone => ast::Constant::None,
|
||||
builtins::slice::PyEllipsis => ast::Constant::Ellipsis,
|
||||
obj =>
|
||||
return Err(vm.new_type_error(format!(
|
||||
"invalid type in Constant: type '{}'",
|
||||
obj.class().name()
|
||||
))),
|
||||
});
|
||||
Ok(constant)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for ast::ConversionFlag {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx.new_int(self as u8).into()
|
||||
}
|
||||
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
i32::try_from_object(vm, object)?
|
||||
.to_u32()
|
||||
.and_then(ast::ConversionFlag::from_op_arg)
|
||||
.ok_or_else(|| vm.new_value_error("invalid conversion flag".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for ast::located::Arguments {
|
||||
fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
self.into_python_arguments().ast_to_object(vm)
|
||||
}
|
||||
fn ast_from_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<Self> {
|
||||
ast::located::PythonArguments::ast_from_object(vm, object)
|
||||
.map(ast::located::PythonArguments::into_arguments)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustpython-parser")]
|
||||
#[cfg(feature = "parser")]
|
||||
pub(crate) fn parse(
|
||||
vm: &VirtualMachine,
|
||||
source: &str,
|
||||
mode: parser::Mode,
|
||||
) -> Result<PyObjectRef, CompileError> {
|
||||
let mut locator = LinearLocator::new(source);
|
||||
let top = parser::parse(source, mode, "<unknown>").map_err(|e| locator.locate_error(e))?;
|
||||
let top = locator.fold_mod(top).unwrap();
|
||||
Ok(top.ast_to_object(vm))
|
||||
let source_code = SourceCodeOwned::new("".to_owned(), source.to_owned());
|
||||
let top = parser::parse(source, mode)
|
||||
.map_err(|parse_error| ParseError {
|
||||
error: parse_error.error,
|
||||
location: text_range_to_source_range(&source_code, parse_error.location)
|
||||
.start
|
||||
.to_source_location(),
|
||||
source_path: "<unknown>".to_string(),
|
||||
})?
|
||||
.into_syntax();
|
||||
let top = match top {
|
||||
ruff::Mod::Module(m) => Mod::Module(m),
|
||||
ruff::Mod::Expression(e) => Mod::Expression(e),
|
||||
};
|
||||
Ok(top.ast_to_object(vm, &source_code))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustpython-codegen")]
|
||||
#[cfg(feature = "codegen")]
|
||||
pub(crate) fn compile(
|
||||
vm: &VirtualMachine,
|
||||
object: PyObjectRef,
|
||||
@@ -348,14 +271,24 @@ pub(crate) fn compile(
|
||||
opts.optimize = optimize;
|
||||
}
|
||||
|
||||
let ast = Node::ast_from_object(vm, object)?;
|
||||
let code = codegen::compile::compile_top(&ast, filename.to_owned(), mode, opts)
|
||||
.map_err(|err| (CompileError::from(err), None).to_pyexception(vm))?; // FIXME source
|
||||
let source_code = SourceCodeOwned::new(filename.to_owned(), "".to_owned());
|
||||
let ast: Mod = Node::ast_from_object(vm, &source_code, object)?;
|
||||
let ast = match ast {
|
||||
Mod::Module(m) => ruff::Mod::Module(m),
|
||||
Mod::Interactive(ModInteractive { range, body }) => {
|
||||
ruff::Mod::Module(ruff::ModModule { range, body })
|
||||
}
|
||||
Mod::Expression(e) => ruff::Mod::Expression(e),
|
||||
Mod::FunctionType(_) => todo!(),
|
||||
};
|
||||
// TODO: create a textual representation of the ast
|
||||
let text = "";
|
||||
let source_code = SourceCode::new(filename, text);
|
||||
let code = codegen::compile::compile_top(ast, source_code, mode, opts)
|
||||
.map_err(|err| vm.new_syntax_error(&err.into(), None))?; // FIXME source
|
||||
Ok(vm.ctx.new_code(code).into())
|
||||
}
|
||||
|
||||
// Required crate visibility for inclusion by gen.rs
|
||||
pub(crate) use _ast::NodeAst;
|
||||
// Used by builtins::compile()
|
||||
pub const PY_COMPILE_FLAG_AST_ONLY: i32 = 0x0400;
|
||||
|
||||
@@ -398,6 +331,6 @@ pub const PY_COMPILE_FLAGS_MASK: i32 = PY_COMPILE_FLAG_AST_ONLY
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
let module = _ast::make_module(vm);
|
||||
r#gen::extend_module_nodes(vm, &module);
|
||||
pyast::extend_module_nodes(vm, &module);
|
||||
module
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) struct PositionalArguments {
|
||||
pub range: TextRange,
|
||||
pub args: Box<[ruff::Expr]>,
|
||||
}
|
||||
|
||||
impl Node for PositionalArguments {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self { args, range: _ } = self;
|
||||
BoxedSlice(args).ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let args: BoxedSlice<_> = Node::ast_from_object(vm, source_code, object)?;
|
||||
Ok(Self {
|
||||
args: args.0,
|
||||
range: TextRange::default(), // TODO
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct KeywordArguments {
|
||||
pub range: TextRange,
|
||||
pub keywords: Box<[ruff::Keyword]>,
|
||||
}
|
||||
|
||||
impl Node for KeywordArguments {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self { keywords, range: _ } = self;
|
||||
// TODO: use range
|
||||
BoxedSlice(keywords).ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let keywords: BoxedSlice<_> = Node::ast_from_object(vm, source_code, object)?;
|
||||
Ok(Self {
|
||||
keywords: keywords.0,
|
||||
range: TextRange::default(), // TODO
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn merge_function_call_arguments(
|
||||
pos_args: PositionalArguments,
|
||||
key_args: KeywordArguments,
|
||||
) -> ruff::Arguments {
|
||||
let range = pos_args.range.cover(key_args.range);
|
||||
|
||||
ruff::Arguments {
|
||||
range,
|
||||
args: pos_args.args,
|
||||
keywords: key_args.keywords,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn split_function_call_arguments(
|
||||
args: ruff::Arguments,
|
||||
) -> (PositionalArguments, KeywordArguments) {
|
||||
let ruff::Arguments {
|
||||
range: _,
|
||||
args,
|
||||
keywords,
|
||||
} = args;
|
||||
|
||||
let positional_arguments_range = args
|
||||
.iter()
|
||||
.map(|item| item.range())
|
||||
.reduce(|acc, next| acc.cover(next))
|
||||
.unwrap_or_default();
|
||||
// debug_assert!(range.contains_range(positional_arguments_range));
|
||||
let positional_arguments = PositionalArguments {
|
||||
range: positional_arguments_range,
|
||||
args,
|
||||
};
|
||||
|
||||
let keyword_arguments_range = keywords
|
||||
.iter()
|
||||
.map(|item| item.range())
|
||||
.reduce(|acc, next| acc.cover(next))
|
||||
.unwrap_or_default();
|
||||
// debug_assert!(range.contains_range(keyword_arguments_range));
|
||||
let keyword_arguments = KeywordArguments {
|
||||
range: keyword_arguments_range,
|
||||
keywords,
|
||||
};
|
||||
|
||||
(positional_arguments, keyword_arguments)
|
||||
}
|
||||
|
||||
pub(super) fn split_class_def_args(
|
||||
args: Option<Box<ruff::Arguments>>,
|
||||
) -> (Option<PositionalArguments>, Option<KeywordArguments>) {
|
||||
let args = match args {
|
||||
None => return (None, None),
|
||||
Some(args) => *args,
|
||||
};
|
||||
let ruff::Arguments {
|
||||
range: _,
|
||||
args,
|
||||
keywords,
|
||||
} = args;
|
||||
|
||||
let positional_arguments_range = args
|
||||
.iter()
|
||||
.map(|item| item.range())
|
||||
.reduce(|acc, next| acc.cover(next))
|
||||
.unwrap_or_default();
|
||||
// debug_assert!(range.contains_range(positional_arguments_range));
|
||||
let positional_arguments = PositionalArguments {
|
||||
range: positional_arguments_range,
|
||||
args,
|
||||
};
|
||||
|
||||
let keyword_arguments_range = keywords
|
||||
.iter()
|
||||
.map(|item| item.range())
|
||||
.reduce(|acc, next| acc.cover(next))
|
||||
.unwrap_or_default();
|
||||
// debug_assert!(range.contains_range(keyword_arguments_range));
|
||||
let keyword_arguments = KeywordArguments {
|
||||
range: keyword_arguments_range,
|
||||
keywords,
|
||||
};
|
||||
|
||||
(Some(positional_arguments), Some(keyword_arguments))
|
||||
}
|
||||
|
||||
pub(super) fn merge_class_def_args(
|
||||
positional_arguments: Option<PositionalArguments>,
|
||||
keyword_arguments: Option<KeywordArguments>,
|
||||
) -> Option<Box<ruff::Arguments>> {
|
||||
if positional_arguments.is_none() && keyword_arguments.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let args = if let Some(positional_arguments) = positional_arguments {
|
||||
positional_arguments.args
|
||||
} else {
|
||||
vec![].into_boxed_slice()
|
||||
};
|
||||
let keywords = if let Some(keyword_arguments) = keyword_arguments {
|
||||
keyword_arguments.keywords
|
||||
} else {
|
||||
vec![].into_boxed_slice()
|
||||
};
|
||||
|
||||
Some(Box::new(ruff::Arguments {
|
||||
range: Default::default(), // TODO
|
||||
args,
|
||||
keywords,
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
use rustpython_codegen::compile::ruff_int_to_bigint;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl Node for ruff::Identifier {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let id = self.as_str();
|
||||
vm.ctx.new_str(id).into()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let py_str = PyStrRef::try_from_object(vm, object)?;
|
||||
Ok(ruff::Identifier::new(py_str.as_str(), TextRange::default()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for ruff::Int {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
vm.ctx.new_int(ruff_int_to_bigint(&self).unwrap()).into()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
// FIXME: performance
|
||||
let value: PyIntRef = object.try_into_value(vm)?;
|
||||
let value = value.as_bigint().to_string();
|
||||
Ok(value.parse().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for bool {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
vm.ctx.new_int(self as u8).into()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
i32::try_from_object(vm, object).map(|i| i != 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
use super::*;
|
||||
use crate::builtins::{PyComplex, PyFrozenSet, PyTuple};
|
||||
use ruff::str_prefix::StringLiteralPrefix;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Constant {
|
||||
pub(super) range: TextRange,
|
||||
pub(super) value: ConstantLiteral,
|
||||
}
|
||||
|
||||
impl Constant {
|
||||
pub(super) fn new_str(
|
||||
value: impl Into<Box<str>>,
|
||||
prefix: StringLiteralPrefix,
|
||||
range: TextRange,
|
||||
) -> Self {
|
||||
let value = value.into();
|
||||
Self {
|
||||
range,
|
||||
value: ConstantLiteral::Str { value, prefix },
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_int(value: ruff::Int, range: TextRange) -> Self {
|
||||
Self {
|
||||
range,
|
||||
value: ConstantLiteral::Int(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_float(value: f64, range: TextRange) -> Self {
|
||||
Self {
|
||||
range,
|
||||
value: ConstantLiteral::Float(value),
|
||||
}
|
||||
}
|
||||
pub(super) fn new_complex(real: f64, imag: f64, range: TextRange) -> Self {
|
||||
Self {
|
||||
range,
|
||||
value: ConstantLiteral::Complex { real, imag },
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_bytes(value: Box<[u8]>, range: TextRange) -> Self {
|
||||
Self {
|
||||
range,
|
||||
value: ConstantLiteral::Bytes(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_bool(value: bool, range: TextRange) -> Self {
|
||||
Self {
|
||||
range,
|
||||
value: ConstantLiteral::Bool(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_none(range: TextRange) -> Self {
|
||||
Self {
|
||||
range,
|
||||
value: ConstantLiteral::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_ellipsis(range: TextRange) -> Self {
|
||||
Self {
|
||||
range,
|
||||
value: ConstantLiteral::Ellipsis,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_expr(self) -> ruff::Expr {
|
||||
constant_to_ruff_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ConstantLiteral {
|
||||
None,
|
||||
Bool(bool),
|
||||
Str {
|
||||
value: Box<str>,
|
||||
prefix: StringLiteralPrefix,
|
||||
},
|
||||
Bytes(Box<[u8]>),
|
||||
Int(ruff::Int),
|
||||
Tuple(Vec<ConstantLiteral>),
|
||||
FrozenSet(Vec<ConstantLiteral>),
|
||||
Float(f64),
|
||||
Complex {
|
||||
real: f64,
|
||||
imag: f64,
|
||||
},
|
||||
Ellipsis,
|
||||
}
|
||||
|
||||
// constructor
|
||||
impl Node for Constant {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self { range, value } = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeExprConstant::static_type().to_owned())
|
||||
.unwrap();
|
||||
let kind = match &value {
|
||||
ConstantLiteral::Str {
|
||||
prefix: StringLiteralPrefix::Unicode,
|
||||
..
|
||||
} => vm.ctx.new_str("u").into(),
|
||||
_ => vm.ctx.none(),
|
||||
};
|
||||
let value = value.ast_to_object(vm, source_code);
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("value", value, vm).unwrap();
|
||||
dict.set_item("kind", kind, vm).unwrap();
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let value_object = get_node_field(vm, &object, "value", "Constant")?;
|
||||
let value = Node::ast_from_object(vm, source_code, value_object)?;
|
||||
|
||||
Ok(Self {
|
||||
value,
|
||||
// kind: get_node_field_opt(_vm, &_object, "kind")?
|
||||
// .map(|obj| Node::ast_from_object(_vm, obj))
|
||||
// .transpose()?,
|
||||
range: range_from_object(vm, source_code, object, "Constant")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for ConstantLiteral {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
match self {
|
||||
ConstantLiteral::None => vm.ctx.none(),
|
||||
ConstantLiteral::Bool(value) => vm.ctx.new_bool(value).to_pyobject(vm),
|
||||
ConstantLiteral::Str { value, .. } => vm.ctx.new_str(value).to_pyobject(vm),
|
||||
ConstantLiteral::Bytes(value) => vm.ctx.new_bytes(value.into()).to_pyobject(vm),
|
||||
ConstantLiteral::Int(value) => value.ast_to_object(vm, source_code),
|
||||
ConstantLiteral::Tuple(value) => {
|
||||
let value = value
|
||||
.into_iter()
|
||||
.map(|c| c.ast_to_object(vm, source_code))
|
||||
.collect();
|
||||
vm.ctx.new_tuple(value).to_pyobject(vm)
|
||||
}
|
||||
ConstantLiteral::FrozenSet(value) => PyFrozenSet::from_iter(
|
||||
vm,
|
||||
value.into_iter().map(|c| c.ast_to_object(vm, source_code)),
|
||||
)
|
||||
.unwrap()
|
||||
.into_pyobject(vm),
|
||||
ConstantLiteral::Float(value) => vm.ctx.new_float(value).into_pyobject(vm),
|
||||
ConstantLiteral::Complex { real, imag } => vm
|
||||
.ctx
|
||||
.new_complex(num_complex::Complex::new(real, imag))
|
||||
.into_pyobject(vm),
|
||||
ConstantLiteral::Ellipsis => vm.ctx.ellipsis(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
value_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let cls = value_object.class();
|
||||
let value = if cls.is(vm.ctx.types.none_type) {
|
||||
ConstantLiteral::None
|
||||
} else if cls.is(vm.ctx.types.bool_type) {
|
||||
ConstantLiteral::Bool(if value_object.is(&vm.ctx.true_value) {
|
||||
true
|
||||
} else if value_object.is(&vm.ctx.false_value) {
|
||||
false
|
||||
} else {
|
||||
value_object.try_to_value(vm)?
|
||||
})
|
||||
} else if cls.is(vm.ctx.types.str_type) {
|
||||
ConstantLiteral::Str {
|
||||
value: value_object.try_to_value::<String>(vm)?.into(),
|
||||
prefix: StringLiteralPrefix::Empty,
|
||||
}
|
||||
} else if cls.is(vm.ctx.types.bytes_type) {
|
||||
ConstantLiteral::Bytes(value_object.try_to_value::<Vec<u8>>(vm)?.into())
|
||||
} else if cls.is(vm.ctx.types.int_type) {
|
||||
ConstantLiteral::Int(Node::ast_from_object(vm, source_code, value_object)?)
|
||||
} else if cls.is(vm.ctx.types.tuple_type) {
|
||||
let tuple = value_object.downcast::<PyTuple>().map_err(|obj| {
|
||||
vm.new_type_error(format!(
|
||||
"Expected type {}, not {}",
|
||||
PyTuple::static_type().name(),
|
||||
obj.class().name()
|
||||
))
|
||||
})?;
|
||||
let tuple = tuple
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.map(|object| Node::ast_from_object(vm, source_code, object))
|
||||
.collect::<PyResult<_>>()?;
|
||||
ConstantLiteral::Tuple(tuple)
|
||||
} else if cls.is(vm.ctx.types.frozenset_type) {
|
||||
let set = value_object.downcast::<PyFrozenSet>().unwrap();
|
||||
let elements = set
|
||||
.elements()
|
||||
.into_iter()
|
||||
.map(|object| Node::ast_from_object(vm, source_code, object))
|
||||
.collect::<PyResult<_>>()?;
|
||||
ConstantLiteral::FrozenSet(elements)
|
||||
} else if cls.is(vm.ctx.types.float_type) {
|
||||
let float = value_object.try_into_value(vm)?;
|
||||
ConstantLiteral::Float(float)
|
||||
} else if cls.is(vm.ctx.types.complex_type) {
|
||||
let complex = value_object.try_complex(vm)?;
|
||||
let complex = match complex {
|
||||
None => {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Expected type {}, not {}",
|
||||
PyComplex::static_type().name(),
|
||||
value_object.class().name()
|
||||
)));
|
||||
}
|
||||
Some((value, _was_coerced)) => value,
|
||||
};
|
||||
ConstantLiteral::Complex {
|
||||
real: complex.re,
|
||||
imag: complex.im,
|
||||
}
|
||||
} else if cls.is(vm.ctx.types.ellipsis_type) {
|
||||
ConstantLiteral::Ellipsis
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"invalid type in Constant: {}",
|
||||
value_object.class().name()
|
||||
)));
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn constant_to_ruff_expr(value: Constant) -> ruff::Expr {
|
||||
let Constant { value, range } = value;
|
||||
match value {
|
||||
ConstantLiteral::None => ruff::Expr::NoneLiteral(ruff::ExprNoneLiteral { range }),
|
||||
ConstantLiteral::Bool(value) => {
|
||||
ruff::Expr::BooleanLiteral(ruff::ExprBooleanLiteral { range, value })
|
||||
}
|
||||
ConstantLiteral::Str { value, prefix } => {
|
||||
ruff::Expr::StringLiteral(ruff::ExprStringLiteral {
|
||||
range,
|
||||
value: ruff::StringLiteralValue::single(ruff::StringLiteral {
|
||||
range,
|
||||
value,
|
||||
flags: ruff::StringLiteralFlags::default().with_prefix(prefix),
|
||||
}),
|
||||
})
|
||||
}
|
||||
ConstantLiteral::Bytes(value) => {
|
||||
ruff::Expr::BytesLiteral(ruff::ExprBytesLiteral {
|
||||
range,
|
||||
value: ruff::BytesLiteralValue::single(ruff::BytesLiteral {
|
||||
range,
|
||||
value,
|
||||
flags: Default::default(), // TODO
|
||||
}),
|
||||
})
|
||||
}
|
||||
ConstantLiteral::Int(value) => ruff::Expr::NumberLiteral(ruff::ExprNumberLiteral {
|
||||
range,
|
||||
value: ruff::Number::Int(value),
|
||||
}),
|
||||
ConstantLiteral::Tuple(value) => ruff::Expr::Tuple(ruff::ExprTuple {
|
||||
range,
|
||||
elts: value
|
||||
.into_iter()
|
||||
.map(|value| {
|
||||
constant_to_ruff_expr(Constant {
|
||||
range: TextRange::default(),
|
||||
value,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
ctx: ruff::ExprContext::Load,
|
||||
// TODO: Does this matter?
|
||||
parenthesized: true,
|
||||
}),
|
||||
ConstantLiteral::FrozenSet(value) => ruff::Expr::Call(ruff::ExprCall {
|
||||
range,
|
||||
// idk lol
|
||||
func: Box::new(ruff::Expr::Name(ruff::ExprName {
|
||||
range: TextRange::default(),
|
||||
id: "frozenset".to_owned(),
|
||||
ctx: ruff::ExprContext::Load,
|
||||
})),
|
||||
arguments: ruff::Arguments {
|
||||
range,
|
||||
args: value
|
||||
.into_iter()
|
||||
.map(|value| {
|
||||
constant_to_ruff_expr(Constant {
|
||||
range: TextRange::default(),
|
||||
value,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
keywords: Box::default(),
|
||||
},
|
||||
}),
|
||||
ConstantLiteral::Float(value) => ruff::Expr::NumberLiteral(ruff::ExprNumberLiteral {
|
||||
range,
|
||||
value: ruff::Number::Float(value),
|
||||
}),
|
||||
ConstantLiteral::Complex { real, imag } => {
|
||||
ruff::Expr::NumberLiteral(ruff::ExprNumberLiteral {
|
||||
range,
|
||||
value: ruff::Number::Complex { real, imag },
|
||||
})
|
||||
}
|
||||
ConstantLiteral::Ellipsis => {
|
||||
ruff::Expr::EllipsisLiteral(ruff::ExprEllipsisLiteral { range })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn number_literal_to_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
constant: ruff::ExprNumberLiteral,
|
||||
) -> PyObjectRef {
|
||||
let ruff::ExprNumberLiteral { range, value } = constant;
|
||||
let c = match value {
|
||||
ruff::Number::Int(n) => Constant::new_int(n, range),
|
||||
ruff::Number::Float(n) => Constant::new_float(n, range),
|
||||
ruff::Number::Complex { real, imag } => Constant::new_complex(real, imag, range),
|
||||
};
|
||||
c.ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
pub(super) fn string_literal_to_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
constant: ruff::ExprStringLiteral,
|
||||
) -> PyObjectRef {
|
||||
let ruff::ExprStringLiteral { range, value } = constant;
|
||||
let prefix = value
|
||||
.iter()
|
||||
.next()
|
||||
.map_or(StringLiteralPrefix::Empty, |part| part.flags.prefix());
|
||||
let c = Constant::new_str(value.to_str(), prefix, range);
|
||||
c.ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
pub(super) fn bytes_literal_to_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
constant: ruff::ExprBytesLiteral,
|
||||
) -> PyObjectRef {
|
||||
let ruff::ExprBytesLiteral { range, value } = constant;
|
||||
let bytes = value.as_slice().iter().flat_map(|b| b.value.iter());
|
||||
let c = Constant::new_bytes(bytes.copied().collect(), range);
|
||||
c.ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
pub(super) fn boolean_literal_to_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
constant: ruff::ExprBooleanLiteral,
|
||||
) -> PyObjectRef {
|
||||
let ruff::ExprBooleanLiteral { range, value } = constant;
|
||||
let c = Constant::new_bool(value, range);
|
||||
c.ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
pub(super) fn none_literal_to_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
constant: ruff::ExprNoneLiteral,
|
||||
) -> PyObjectRef {
|
||||
let ruff::ExprNoneLiteral { range } = constant;
|
||||
let c = Constant::new_none(range);
|
||||
c.ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
pub(super) fn ellipsis_literal_to_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
constant: ruff::ExprEllipsisLiteral,
|
||||
) -> PyObjectRef {
|
||||
let ruff::ExprEllipsisLiteral { range } = constant;
|
||||
let c = Constant::new_ellipsis(range);
|
||||
c.ast_to_object(vm, source_code)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) fn ast_to_object(
|
||||
clause: ruff::ElifElseClause,
|
||||
mut rest: std::vec::IntoIter<ruff::ElifElseClause>,
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
) -> PyObjectRef {
|
||||
let ruff::ElifElseClause { range, test, body } = clause;
|
||||
let Some(test) = test else {
|
||||
assert!(rest.len() == 0);
|
||||
return body.ast_to_object(vm, source_code);
|
||||
};
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeStmtIf::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
|
||||
dict.set_item("test", test.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item("body", body.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
|
||||
let orelse = if let Some(next) = rest.next() {
|
||||
if next.test.is_some() {
|
||||
vm.ctx
|
||||
.new_list(vec![ast_to_object(next, rest, vm, source_code)])
|
||||
.into()
|
||||
} else {
|
||||
next.body.ast_to_object(vm, source_code)
|
||||
}
|
||||
} else {
|
||||
vm.ctx.new_list(vec![]).into()
|
||||
};
|
||||
dict.set_item("orelse", orelse, vm).unwrap();
|
||||
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
|
||||
pub(super) fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<ruff::StmtIf> {
|
||||
let test = Node::ast_from_object(vm, source_code, get_node_field(vm, &object, "test", "If")?)?;
|
||||
let body = Node::ast_from_object(vm, source_code, get_node_field(vm, &object, "body", "If")?)?;
|
||||
let orelse: Vec<ruff::Stmt> = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "orelse", "If")?,
|
||||
)?;
|
||||
let range = range_from_object(vm, source_code, object, "If")?;
|
||||
|
||||
let elif_else_clauses = if let [ruff::Stmt::If(_)] = &*orelse {
|
||||
let Some(ruff::Stmt::If(ruff::StmtIf {
|
||||
range,
|
||||
test,
|
||||
body,
|
||||
mut elif_else_clauses,
|
||||
})) = orelse.into_iter().next()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
elif_else_clauses.insert(
|
||||
0,
|
||||
ruff::ElifElseClause {
|
||||
range,
|
||||
test: Some(*test),
|
||||
body,
|
||||
},
|
||||
);
|
||||
elif_else_clauses
|
||||
} else {
|
||||
vec![ruff::ElifElseClause {
|
||||
range,
|
||||
test: None,
|
||||
body: orelse,
|
||||
}]
|
||||
};
|
||||
|
||||
Ok(ruff::StmtIf {
|
||||
test,
|
||||
body,
|
||||
elif_else_clauses,
|
||||
range,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
use super::*;
|
||||
|
||||
// sum
|
||||
impl Node for ruff::ExceptHandler {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
match self {
|
||||
ruff::ExceptHandler::ExceptHandler(cons) => cons.ast_to_object(vm, source_code),
|
||||
}
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let _cls = _object.class();
|
||||
Ok(
|
||||
if _cls.is(pyast::NodeExceptHandlerExceptHandler::static_type()) {
|
||||
ruff::ExceptHandler::ExceptHandler(
|
||||
ruff::ExceptHandlerExceptHandler::ast_from_object(_vm, source_code, _object)?,
|
||||
)
|
||||
} else {
|
||||
return Err(_vm.new_type_error(format!(
|
||||
"expected some sort of excepthandler, but got {}",
|
||||
_object.repr(_vm)?
|
||||
)));
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::ExceptHandlerExceptHandler {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let ruff::ExceptHandlerExceptHandler {
|
||||
type_,
|
||||
name,
|
||||
body,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(
|
||||
_vm,
|
||||
pyast::NodeExceptHandlerExceptHandler::static_type().to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("type", type_.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("name", name.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("body", body.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(ruff::ExceptHandlerExceptHandler {
|
||||
type_: get_node_field_opt(_vm, &_object, "type")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
name: get_node_field_opt(_vm, &_object, "name")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
body: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "body", "ExceptHandler")?,
|
||||
)?,
|
||||
range: range_from_object(_vm, source_code, _object, "ExceptHandler")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Generated
-5496
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,221 @@
|
||||
use super::*;
|
||||
use crate::stdlib::ast::type_ignore::TypeIgnore;
|
||||
|
||||
/// Represents the different types of Python module structures.
|
||||
///
|
||||
/// This enum is used to represent the various possible forms of a Python module
|
||||
/// in an Abstract Syntax Tree (AST). It can correspond to:
|
||||
///
|
||||
/// - `Module`: A standard Python script, containing a sequence of statements
|
||||
/// (e.g., assignments, function calls), possibly with type ignores.
|
||||
/// - `Interactive`: A representation of code executed in an interactive
|
||||
/// Python session (e.g., the REPL or Jupyter notebooks), where statements
|
||||
/// are evaluated one at a time.
|
||||
/// - `Expression`: A single expression without any surrounding statements.
|
||||
/// This is typically used in scenarios like `eval()` or in expression-only
|
||||
/// contexts.
|
||||
/// - `FunctionType`: A function signature with argument and return type
|
||||
/// annotations, representing the type hints of a function (e.g., `def add(x: int, y: int) -> int`).
|
||||
pub(super) enum Mod {
|
||||
Module(ruff::ModModule),
|
||||
Interactive(ModInteractive),
|
||||
Expression(ruff::ModExpression),
|
||||
FunctionType(ModFunctionType),
|
||||
}
|
||||
|
||||
// sum
|
||||
impl Node for Mod {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
match self {
|
||||
Self::Module(cons) => cons.ast_to_object(vm, source_code),
|
||||
Self::Interactive(cons) => cons.ast_to_object(vm, source_code),
|
||||
Self::Expression(cons) => cons.ast_to_object(vm, source_code),
|
||||
Self::FunctionType(cons) => cons.ast_to_object(vm, source_code),
|
||||
}
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let cls = object.class();
|
||||
Ok(if cls.is(pyast::NodeModModule::static_type()) {
|
||||
Self::Module(ruff::ModModule::ast_from_object(vm, source_code, object)?)
|
||||
} else if cls.is(pyast::NodeModInteractive::static_type()) {
|
||||
Self::Interactive(ModInteractive::ast_from_object(vm, source_code, object)?)
|
||||
} else if cls.is(pyast::NodeModExpression::static_type()) {
|
||||
Self::Expression(ruff::ModExpression::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
object,
|
||||
)?)
|
||||
} else if cls.is(pyast::NodeModFunctionType::static_type()) {
|
||||
Self::FunctionType(ModFunctionType::ast_from_object(vm, source_code, object)?)
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"expected some sort of mod, but got {}",
|
||||
object.repr(vm)?
|
||||
)));
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::ModModule {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let ruff::ModModule {
|
||||
body,
|
||||
// type_ignores,
|
||||
range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeModModule::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("body", body.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
// TODO: Improve ruff API
|
||||
// ruff ignores type_ignore comments currently.
|
||||
let type_ignores: Vec<TypeIgnore> = vec![];
|
||||
dict.set_item(
|
||||
"type_ignores",
|
||||
type_ignores.ast_to_object(vm, source_code),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(ruff::ModModule {
|
||||
body: Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "body", "Module")?,
|
||||
)?,
|
||||
// type_ignores: Node::ast_from_object(
|
||||
// _vm,
|
||||
// get_node_field(_vm, &_object, "type_ignores", "Module")?,
|
||||
// )?,
|
||||
range: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct ModInteractive {
|
||||
pub(crate) range: TextRange,
|
||||
pub(crate) body: Vec<ruff::Stmt>,
|
||||
}
|
||||
|
||||
// constructor
|
||||
impl Node for ModInteractive {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self { body, range } = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeModInteractive::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("body", body.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
body: Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "body", "Interactive")?,
|
||||
)?,
|
||||
range: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::ModExpression {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self { body, range } = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeModExpression::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("body", body.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
body: Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "body", "Expression")?,
|
||||
)?,
|
||||
range: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct ModFunctionType {
|
||||
pub(crate) argtypes: Box<[ruff::Expr]>,
|
||||
pub(crate) returns: ruff::Expr,
|
||||
pub(crate) range: TextRange,
|
||||
}
|
||||
|
||||
// constructor
|
||||
impl Node for ModFunctionType {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let ModFunctionType {
|
||||
argtypes,
|
||||
returns,
|
||||
range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeModFunctionType::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item(
|
||||
"argtypes",
|
||||
BoxedSlice(argtypes).ast_to_object(vm, source_code),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
dict.set_item("returns", returns.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(ModFunctionType {
|
||||
argtypes: {
|
||||
let argtypes: BoxedSlice<_> = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "argtypes", "FunctionType")?,
|
||||
)?;
|
||||
argtypes.0
|
||||
},
|
||||
returns: Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "returns", "FunctionType")?,
|
||||
)?,
|
||||
range: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
use crate::{PyObjectRef, PyResult, VirtualMachine};
|
||||
use rustpython_compiler_source::SourceCodeOwned;
|
||||
|
||||
pub(crate) trait Node: Sized {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef;
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self>;
|
||||
/// Used in `Option::ast_from_object`; if `true`, that impl will return None.
|
||||
fn is_none(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Node> Node for Vec<T> {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
vm.ctx
|
||||
.new_list(
|
||||
self.into_iter()
|
||||
.map(|node| node.ast_to_object(vm, source_code))
|
||||
.collect(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
vm.extract_elements_with(&object, |obj| Node::ast_from_object(vm, source_code, obj))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Node> Node for Box<T> {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
(*self).ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
T::ast_from_object(vm, source_code, object).map(Box::new)
|
||||
}
|
||||
|
||||
fn is_none(&self) -> bool {
|
||||
(**self).is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Node> Node for Option<T> {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
match self {
|
||||
Some(node) => node.ast_to_object(vm, source_code),
|
||||
None => vm.ctx.none(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
if vm.is_none(&object) {
|
||||
Ok(None)
|
||||
} else {
|
||||
let x = T::ast_from_object(vm, source_code, object)?;
|
||||
Ok((!x.is_none()).then_some(x))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct BoxedSlice<T>(pub(super) Box<[T]>);
|
||||
|
||||
impl<T: Node> Node for BoxedSlice<T> {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
self.0.into_vec().ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self(
|
||||
<Vec<T> as Node>::ast_from_object(vm, source_code, object)?.into_boxed_slice(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
use super::*;
|
||||
|
||||
// sum
|
||||
impl Node for ruff::BoolOp {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let node_type = match self {
|
||||
ruff::BoolOp::And => pyast::NodeBoolOpAnd::static_type(),
|
||||
ruff::BoolOp::Or => pyast::NodeBoolOpOr::static_type(),
|
||||
};
|
||||
NodeAst
|
||||
.into_ref_with_type(vm, node_type.to_owned())
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let _cls = _object.class();
|
||||
Ok(if _cls.is(pyast::NodeBoolOpAnd::static_type()) {
|
||||
ruff::BoolOp::And
|
||||
} else if _cls.is(pyast::NodeBoolOpOr::static_type()) {
|
||||
ruff::BoolOp::Or
|
||||
} else {
|
||||
return Err(_vm.new_type_error(format!(
|
||||
"expected some sort of boolop, but got {}",
|
||||
_object.repr(_vm)?
|
||||
)));
|
||||
})
|
||||
}
|
||||
}
|
||||
// sum
|
||||
impl Node for ruff::Operator {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let node_type = match self {
|
||||
ruff::Operator::Add => pyast::NodeOperatorAdd::static_type(),
|
||||
ruff::Operator::Sub => pyast::NodeOperatorSub::static_type(),
|
||||
ruff::Operator::Mult => pyast::NodeOperatorMult::static_type(),
|
||||
ruff::Operator::MatMult => pyast::NodeOperatorMatMult::static_type(),
|
||||
ruff::Operator::Div => pyast::NodeOperatorDiv::static_type(),
|
||||
ruff::Operator::Mod => pyast::NodeOperatorMod::static_type(),
|
||||
ruff::Operator::Pow => pyast::NodeOperatorPow::static_type(),
|
||||
ruff::Operator::LShift => pyast::NodeOperatorLShift::static_type(),
|
||||
ruff::Operator::RShift => pyast::NodeOperatorRShift::static_type(),
|
||||
ruff::Operator::BitOr => pyast::NodeOperatorBitOr::static_type(),
|
||||
ruff::Operator::BitXor => pyast::NodeOperatorBitXor::static_type(),
|
||||
ruff::Operator::BitAnd => pyast::NodeOperatorBitAnd::static_type(),
|
||||
ruff::Operator::FloorDiv => pyast::NodeOperatorFloorDiv::static_type(),
|
||||
};
|
||||
NodeAst
|
||||
.into_ref_with_type(vm, node_type.to_owned())
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let _cls = _object.class();
|
||||
Ok(if _cls.is(pyast::NodeOperatorAdd::static_type()) {
|
||||
ruff::Operator::Add
|
||||
} else if _cls.is(pyast::NodeOperatorSub::static_type()) {
|
||||
ruff::Operator::Sub
|
||||
} else if _cls.is(pyast::NodeOperatorMult::static_type()) {
|
||||
ruff::Operator::Mult
|
||||
} else if _cls.is(pyast::NodeOperatorMatMult::static_type()) {
|
||||
ruff::Operator::MatMult
|
||||
} else if _cls.is(pyast::NodeOperatorDiv::static_type()) {
|
||||
ruff::Operator::Div
|
||||
} else if _cls.is(pyast::NodeOperatorMod::static_type()) {
|
||||
ruff::Operator::Mod
|
||||
} else if _cls.is(pyast::NodeOperatorPow::static_type()) {
|
||||
ruff::Operator::Pow
|
||||
} else if _cls.is(pyast::NodeOperatorLShift::static_type()) {
|
||||
ruff::Operator::LShift
|
||||
} else if _cls.is(pyast::NodeOperatorRShift::static_type()) {
|
||||
ruff::Operator::RShift
|
||||
} else if _cls.is(pyast::NodeOperatorBitOr::static_type()) {
|
||||
ruff::Operator::BitOr
|
||||
} else if _cls.is(pyast::NodeOperatorBitXor::static_type()) {
|
||||
ruff::Operator::BitXor
|
||||
} else if _cls.is(pyast::NodeOperatorBitAnd::static_type()) {
|
||||
ruff::Operator::BitAnd
|
||||
} else if _cls.is(pyast::NodeOperatorFloorDiv::static_type()) {
|
||||
ruff::Operator::FloorDiv
|
||||
} else {
|
||||
return Err(_vm.new_type_error(format!(
|
||||
"expected some sort of operator, but got {}",
|
||||
_object.repr(_vm)?
|
||||
)));
|
||||
})
|
||||
}
|
||||
}
|
||||
// sum
|
||||
impl Node for ruff::UnaryOp {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let node_type = match self {
|
||||
ruff::UnaryOp::Invert => pyast::NodeUnaryOpInvert::static_type(),
|
||||
ruff::UnaryOp::Not => pyast::NodeUnaryOpNot::static_type(),
|
||||
ruff::UnaryOp::UAdd => pyast::NodeUnaryOpUAdd::static_type(),
|
||||
ruff::UnaryOp::USub => pyast::NodeUnaryOpUSub::static_type(),
|
||||
};
|
||||
NodeAst
|
||||
.into_ref_with_type(vm, node_type.to_owned())
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let _cls = _object.class();
|
||||
Ok(if _cls.is(pyast::NodeUnaryOpInvert::static_type()) {
|
||||
ruff::UnaryOp::Invert
|
||||
} else if _cls.is(pyast::NodeUnaryOpNot::static_type()) {
|
||||
ruff::UnaryOp::Not
|
||||
} else if _cls.is(pyast::NodeUnaryOpUAdd::static_type()) {
|
||||
ruff::UnaryOp::UAdd
|
||||
} else if _cls.is(pyast::NodeUnaryOpUSub::static_type()) {
|
||||
ruff::UnaryOp::USub
|
||||
} else {
|
||||
return Err(_vm.new_type_error(format!(
|
||||
"expected some sort of unaryop, but got {}",
|
||||
_object.repr(_vm)?
|
||||
)));
|
||||
})
|
||||
}
|
||||
}
|
||||
// sum
|
||||
impl Node for ruff::CmpOp {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let node_type = match self {
|
||||
ruff::CmpOp::Eq => pyast::NodeCmpOpEq::static_type(),
|
||||
ruff::CmpOp::NotEq => pyast::NodeCmpOpNotEq::static_type(),
|
||||
ruff::CmpOp::Lt => pyast::NodeCmpOpLt::static_type(),
|
||||
ruff::CmpOp::LtE => pyast::NodeCmpOpLtE::static_type(),
|
||||
ruff::CmpOp::Gt => pyast::NodeCmpOpGt::static_type(),
|
||||
ruff::CmpOp::GtE => pyast::NodeCmpOpGtE::static_type(),
|
||||
ruff::CmpOp::Is => pyast::NodeCmpOpIs::static_type(),
|
||||
ruff::CmpOp::IsNot => pyast::NodeCmpOpIsNot::static_type(),
|
||||
ruff::CmpOp::In => pyast::NodeCmpOpIn::static_type(),
|
||||
ruff::CmpOp::NotIn => pyast::NodeCmpOpNotIn::static_type(),
|
||||
};
|
||||
NodeAst
|
||||
.into_ref_with_type(vm, node_type.to_owned())
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let _cls = _object.class();
|
||||
Ok(if _cls.is(pyast::NodeCmpOpEq::static_type()) {
|
||||
ruff::CmpOp::Eq
|
||||
} else if _cls.is(pyast::NodeCmpOpNotEq::static_type()) {
|
||||
ruff::CmpOp::NotEq
|
||||
} else if _cls.is(pyast::NodeCmpOpLt::static_type()) {
|
||||
ruff::CmpOp::Lt
|
||||
} else if _cls.is(pyast::NodeCmpOpLtE::static_type()) {
|
||||
ruff::CmpOp::LtE
|
||||
} else if _cls.is(pyast::NodeCmpOpGt::static_type()) {
|
||||
ruff::CmpOp::Gt
|
||||
} else if _cls.is(pyast::NodeCmpOpGtE::static_type()) {
|
||||
ruff::CmpOp::GtE
|
||||
} else if _cls.is(pyast::NodeCmpOpIs::static_type()) {
|
||||
ruff::CmpOp::Is
|
||||
} else if _cls.is(pyast::NodeCmpOpIsNot::static_type()) {
|
||||
ruff::CmpOp::IsNot
|
||||
} else if _cls.is(pyast::NodeCmpOpIn::static_type()) {
|
||||
ruff::CmpOp::In
|
||||
} else if _cls.is(pyast::NodeCmpOpNotIn::static_type()) {
|
||||
ruff::CmpOp::NotIn
|
||||
} else {
|
||||
return Err(_vm.new_type_error(format!(
|
||||
"expected some sort of cmpop, but got {}",
|
||||
_object.repr(_vm)?
|
||||
)));
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
use super::*;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
impl Node for ruff::ConversionFlag {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
vm.ctx.new_int(self as u8).into()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
i32::try_from_object(vm, object)?
|
||||
.to_u32()
|
||||
.and_then(ruff::ConversionFlag::from_op_arg)
|
||||
.ok_or_else(|| vm.new_value_error("invalid conversion flag".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
// /// This is just a string, not strictly an AST node. But it makes AST conversions easier.
|
||||
// impl Node for ruff::name::Name {
|
||||
// fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
// vm.ctx.new_str(self.as_str()).to_pyobject(vm)
|
||||
// }
|
||||
|
||||
// fn ast_from_object(vm: &VirtualMachine, source_code: &SourceCodeOwned, object: PyObjectRef) -> PyResult<Self> {
|
||||
// match object.downcast::<PyStr>() {
|
||||
// Ok(name) => Ok(Self::new(name)),
|
||||
// Err(_) => Err(vm.new_value_error("expected str for name".to_owned())),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Node for ruff::Decorator {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
ruff::Expr::ast_to_object(self.expression, vm, source_code)
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let expression = ruff::Expr::ast_from_object(vm, source_code, object)?;
|
||||
let range = expression.range();
|
||||
Ok(ruff::Decorator { expression, range })
|
||||
}
|
||||
}
|
||||
|
||||
// product
|
||||
impl Node for ruff::Alias {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
name,
|
||||
asname,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeAlias::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("name", name.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item("asname", asname.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
name: Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "name", "alias")?,
|
||||
)?,
|
||||
asname: get_node_field_opt(vm, &object, "asname")?
|
||||
.map(|obj| Node::ast_from_object(vm, source_code, obj))
|
||||
.transpose()?,
|
||||
range: range_from_object(vm, source_code, object, "alias")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
// product
|
||||
impl Node for ruff::WithItem {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
context_expr,
|
||||
optional_vars,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeWithItem::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item(
|
||||
"context_expr",
|
||||
context_expr.ast_to_object(vm, source_code),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"optional_vars",
|
||||
optional_vars.ast_to_object(vm, source_code),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
context_expr: Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "context_expr", "withitem")?,
|
||||
)?,
|
||||
optional_vars: get_node_field_opt(vm, &object, "optional_vars")?
|
||||
.map(|obj| Node::ast_from_object(vm, source_code, obj))
|
||||
.transpose()?,
|
||||
range: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
use super::*;
|
||||
|
||||
// product
|
||||
impl Node for ruff::Parameters {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
posonlyargs,
|
||||
args,
|
||||
vararg,
|
||||
kwonlyargs,
|
||||
kwarg,
|
||||
range,
|
||||
} = self;
|
||||
let (posonlyargs, args, defaults) =
|
||||
extract_positional_parameter_defaults(posonlyargs, args);
|
||||
let (kwonlyargs, kw_defaults) = extract_keyword_parameter_defaults(kwonlyargs);
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeArguments::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item(
|
||||
"posonlyargs",
|
||||
posonlyargs.ast_to_object(vm, source_code),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
dict.set_item("args", args.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item("vararg", vararg.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item("kwonlyargs", kwonlyargs.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"kw_defaults",
|
||||
kw_defaults.ast_to_object(vm, source_code),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
dict.set_item("kwarg", kwarg.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item("defaults", defaults.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let kwonlyargs = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "kwonlyargs", "arguments")?,
|
||||
)?;
|
||||
let kw_defaults = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "kw_defaults", "arguments")?,
|
||||
)?;
|
||||
let kwonlyargs = merge_keyword_parameter_defaults(kwonlyargs, kw_defaults);
|
||||
|
||||
let posonlyargs = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "posonlyargs", "arguments")?,
|
||||
)?;
|
||||
let args = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "args", "arguments")?,
|
||||
)?;
|
||||
let defaults = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "defaults", "arguments")?,
|
||||
)?;
|
||||
let (posonlyargs, args) = merge_positional_parameter_defaults(posonlyargs, args, defaults);
|
||||
|
||||
Ok(Self {
|
||||
posonlyargs,
|
||||
args,
|
||||
vararg: get_node_field_opt(vm, &object, "vararg")?
|
||||
.map(|obj| Node::ast_from_object(vm, source_code, obj))
|
||||
.transpose()?,
|
||||
kwonlyargs,
|
||||
kwarg: get_node_field_opt(vm, &object, "kwarg")?
|
||||
.map(|obj| Node::ast_from_object(vm, source_code, obj))
|
||||
.transpose()?,
|
||||
range: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn is_none(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
||||
// product
|
||||
impl Node for ruff::Parameter {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
name,
|
||||
annotation,
|
||||
// type_comment,
|
||||
range,
|
||||
} = self;
|
||||
|
||||
// ruff covers the ** in range but python expects it to start at the ident
|
||||
let range = TextRange::new(name.start(), range.end());
|
||||
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodeArg::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("arg", name.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"annotation",
|
||||
annotation.ast_to_object(_vm, source_code),
|
||||
_vm,
|
||||
)
|
||||
.unwrap();
|
||||
// dict.set_item("type_comment", type_comment.ast_to_object(_vm), _vm)
|
||||
// .unwrap();
|
||||
node_add_location(&dict, range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
name: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "arg", "arg")?,
|
||||
)?,
|
||||
annotation: get_node_field_opt(_vm, &_object, "annotation")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
// type_comment: get_node_field_opt(_vm, &_object, "type_comment")?
|
||||
// .map(|obj| Node::ast_from_object(_vm, obj))
|
||||
// .transpose()?,
|
||||
range: range_from_object(_vm, source_code, _object, "arg")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// product
|
||||
impl Node for ruff::Keyword {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
arg,
|
||||
value,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodeKeyword::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("arg", arg.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("value", value.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
arg: get_node_field_opt(_vm, &_object, "arg")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
value: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "value", "keyword")?,
|
||||
)?,
|
||||
range: range_from_object(_vm, source_code, _object, "keyword")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct PositionalParameters {
|
||||
pub _range: TextRange, // TODO: Use this
|
||||
pub args: Box<[ruff::Parameter]>,
|
||||
}
|
||||
|
||||
impl Node for PositionalParameters {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
BoxedSlice(self.args).ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let args: BoxedSlice<_> = Node::ast_from_object(vm, source_code, object)?;
|
||||
Ok(Self {
|
||||
args: args.0,
|
||||
_range: TextRange::default(), // TODO
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct KeywordParameters {
|
||||
pub _range: TextRange, // TODO: Use this
|
||||
pub keywords: Box<[ruff::Parameter]>,
|
||||
}
|
||||
|
||||
impl Node for KeywordParameters {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
BoxedSlice(self.keywords).ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let keywords: BoxedSlice<_> = Node::ast_from_object(vm, source_code, object)?;
|
||||
Ok(Self {
|
||||
keywords: keywords.0,
|
||||
_range: TextRange::default(), // TODO
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ParameterDefaults {
|
||||
pub _range: TextRange, // TODO: Use this
|
||||
defaults: Box<[Option<Box<ruff::Expr>>]>,
|
||||
}
|
||||
|
||||
impl Node for ParameterDefaults {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
BoxedSlice(self.defaults).ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let defaults: BoxedSlice<_> = Node::ast_from_object(vm, source_code, object)?;
|
||||
Ok(Self {
|
||||
defaults: defaults.0,
|
||||
_range: TextRange::default(), // TODO
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_positional_parameter_defaults(
|
||||
pos_only_args: Vec<ruff::ParameterWithDefault>,
|
||||
args: Vec<ruff::ParameterWithDefault>,
|
||||
) -> (
|
||||
PositionalParameters,
|
||||
PositionalParameters,
|
||||
ParameterDefaults,
|
||||
) {
|
||||
let mut defaults = vec![];
|
||||
defaults.extend(pos_only_args.iter().map(|item| item.default.clone()));
|
||||
defaults.extend(args.iter().map(|item| item.default.clone()));
|
||||
// If some positional parameters have no default value,
|
||||
// the "defaults" list contains only the defaults of the last "n" parameters.
|
||||
// Remove all positional parameters without a default value.
|
||||
defaults.retain(Option::is_some);
|
||||
let defaults = ParameterDefaults {
|
||||
_range: defaults
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|item| item.range())
|
||||
.reduce(|acc, next| acc.cover(next))
|
||||
.unwrap_or_default(),
|
||||
defaults: defaults.into_boxed_slice(),
|
||||
};
|
||||
|
||||
let pos_only_args = PositionalParameters {
|
||||
_range: pos_only_args
|
||||
.iter()
|
||||
.map(|item| item.range())
|
||||
.reduce(|acc, next| acc.cover(next))
|
||||
.unwrap_or_default(),
|
||||
args: {
|
||||
let pos_only_args: Vec<_> = pos_only_args
|
||||
.iter()
|
||||
.map(|item| item.parameter.clone())
|
||||
.collect();
|
||||
pos_only_args.into_boxed_slice()
|
||||
},
|
||||
};
|
||||
|
||||
let args = PositionalParameters {
|
||||
_range: args
|
||||
.iter()
|
||||
.map(|item| item.range())
|
||||
.reduce(|acc, next| acc.cover(next))
|
||||
.unwrap_or_default(),
|
||||
args: {
|
||||
let args: Vec<_> = args.iter().map(|item| item.parameter.clone()).collect();
|
||||
args.into_boxed_slice()
|
||||
},
|
||||
};
|
||||
|
||||
(pos_only_args, args, defaults)
|
||||
}
|
||||
|
||||
/// Merges the keyword parameters with their default values, opposite of [`extract_positional_parameter_defaults`].
|
||||
fn merge_positional_parameter_defaults(
|
||||
posonlyargs: PositionalParameters,
|
||||
args: PositionalParameters,
|
||||
defaults: ParameterDefaults,
|
||||
) -> (
|
||||
Vec<ruff::ParameterWithDefault>,
|
||||
Vec<ruff::ParameterWithDefault>,
|
||||
) {
|
||||
let posonlyargs = posonlyargs.args;
|
||||
let args = args.args;
|
||||
let defaults = defaults.defaults;
|
||||
|
||||
let mut posonlyargs: Vec<_> = <Box<[_]> as IntoIterator>::into_iter(posonlyargs)
|
||||
.map(|parameter| ruff::ParameterWithDefault {
|
||||
range: Default::default(),
|
||||
parameter,
|
||||
default: None,
|
||||
})
|
||||
.collect();
|
||||
let mut args: Vec<_> = <Box<[_]> as IntoIterator>::into_iter(args)
|
||||
.map(|parameter| ruff::ParameterWithDefault {
|
||||
range: Default::default(),
|
||||
parameter,
|
||||
default: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// If an argument has a default value, insert it
|
||||
// Note that "defaults" will only contain default values for the last "n" parameters
|
||||
// so we need to skip the first "total_argument_count - n" arguments.
|
||||
let default_argument_count = posonlyargs.len() + args.len() - defaults.len();
|
||||
for (arg, default) in posonlyargs
|
||||
.iter_mut()
|
||||
.chain(args.iter_mut())
|
||||
.skip(default_argument_count)
|
||||
.zip(defaults)
|
||||
{
|
||||
arg.default = default;
|
||||
}
|
||||
|
||||
(posonlyargs, args)
|
||||
}
|
||||
|
||||
fn extract_keyword_parameter_defaults(
|
||||
kw_only_args: Vec<ruff::ParameterWithDefault>,
|
||||
) -> (KeywordParameters, ParameterDefaults) {
|
||||
let mut defaults = vec![];
|
||||
defaults.extend(kw_only_args.iter().map(|item| item.default.clone()));
|
||||
let defaults = ParameterDefaults {
|
||||
_range: defaults
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|item| item.range())
|
||||
.reduce(|acc, next| acc.cover(next))
|
||||
.unwrap_or_default(),
|
||||
defaults: defaults.into_boxed_slice(),
|
||||
};
|
||||
|
||||
let kw_only_args = KeywordParameters {
|
||||
_range: kw_only_args
|
||||
.iter()
|
||||
.map(|item| item.range())
|
||||
.reduce(|acc, next| acc.cover(next))
|
||||
.unwrap_or_default(),
|
||||
keywords: {
|
||||
let kw_only_args: Vec<_> = kw_only_args
|
||||
.iter()
|
||||
.map(|item| item.parameter.clone())
|
||||
.collect();
|
||||
kw_only_args.into_boxed_slice()
|
||||
},
|
||||
};
|
||||
|
||||
(kw_only_args, defaults)
|
||||
}
|
||||
|
||||
/// Merges the keyword parameters with their default values, opposite of [`extract_keyword_parameter_defaults`].
|
||||
fn merge_keyword_parameter_defaults(
|
||||
kw_only_args: KeywordParameters,
|
||||
defaults: ParameterDefaults,
|
||||
) -> Vec<ruff::ParameterWithDefault> {
|
||||
std::iter::zip(kw_only_args.keywords, defaults.defaults)
|
||||
.map(|(parameter, default)| ruff::ParameterWithDefault {
|
||||
parameter,
|
||||
default,
|
||||
range: Default::default(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
use super::*;
|
||||
|
||||
// product
|
||||
impl Node for ruff::MatchCase {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
pattern,
|
||||
guard,
|
||||
body,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodeMatchCase::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("pattern", pattern.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("guard", guard.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("body", body.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
pattern: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "pattern", "match_case")?,
|
||||
)?,
|
||||
guard: get_node_field_opt(_vm, &_object, "guard")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
body: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "body", "match_case")?,
|
||||
)?,
|
||||
range: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
// sum
|
||||
impl Node for ruff::Pattern {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
match self {
|
||||
ruff::Pattern::MatchValue(cons) => cons.ast_to_object(vm, source_code),
|
||||
ruff::Pattern::MatchSingleton(cons) => cons.ast_to_object(vm, source_code),
|
||||
ruff::Pattern::MatchSequence(cons) => cons.ast_to_object(vm, source_code),
|
||||
ruff::Pattern::MatchMapping(cons) => cons.ast_to_object(vm, source_code),
|
||||
ruff::Pattern::MatchClass(cons) => cons.ast_to_object(vm, source_code),
|
||||
ruff::Pattern::MatchStar(cons) => cons.ast_to_object(vm, source_code),
|
||||
ruff::Pattern::MatchAs(cons) => cons.ast_to_object(vm, source_code),
|
||||
ruff::Pattern::MatchOr(cons) => cons.ast_to_object(vm, source_code),
|
||||
}
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let _cls = _object.class();
|
||||
Ok(if _cls.is(pyast::NodePatternMatchValue::static_type()) {
|
||||
ruff::Pattern::MatchValue(ruff::PatternMatchValue::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else if _cls.is(pyast::NodePatternMatchSingleton::static_type()) {
|
||||
ruff::Pattern::MatchSingleton(ruff::PatternMatchSingleton::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else if _cls.is(pyast::NodePatternMatchSequence::static_type()) {
|
||||
ruff::Pattern::MatchSequence(ruff::PatternMatchSequence::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else if _cls.is(pyast::NodePatternMatchMapping::static_type()) {
|
||||
ruff::Pattern::MatchMapping(ruff::PatternMatchMapping::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else if _cls.is(pyast::NodePatternMatchClass::static_type()) {
|
||||
ruff::Pattern::MatchClass(ruff::PatternMatchClass::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else if _cls.is(pyast::NodePatternMatchStar::static_type()) {
|
||||
ruff::Pattern::MatchStar(ruff::PatternMatchStar::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else if _cls.is(pyast::NodePatternMatchAs::static_type()) {
|
||||
ruff::Pattern::MatchAs(ruff::PatternMatchAs::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else if _cls.is(pyast::NodePatternMatchOr::static_type()) {
|
||||
ruff::Pattern::MatchOr(ruff::PatternMatchOr::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else {
|
||||
return Err(_vm.new_type_error(format!(
|
||||
"expected some sort of pattern, but got {}",
|
||||
_object.repr(_vm)?
|
||||
)));
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::PatternMatchValue {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
value,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodePatternMatchValue::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("value", value.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
value: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "value", "MatchValue")?,
|
||||
)?,
|
||||
range: range_from_object(_vm, source_code, _object, "MatchValue")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::PatternMatchSingleton {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
value,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(
|
||||
_vm,
|
||||
pyast::NodePatternMatchSingleton::static_type().to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("value", value.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
value: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "value", "MatchSingleton")?,
|
||||
)?,
|
||||
range: range_from_object(_vm, source_code, _object, "MatchSingleton")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl Node for ruff::Singleton {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::PatternMatchSequence {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
patterns,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(
|
||||
_vm,
|
||||
pyast::NodePatternMatchSequence::static_type().to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("patterns", patterns.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
patterns: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "patterns", "MatchSequence")?,
|
||||
)?,
|
||||
range: range_from_object(_vm, source_code, _object, "MatchSequence")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::PatternMatchMapping {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
keys,
|
||||
patterns,
|
||||
rest,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(
|
||||
_vm,
|
||||
pyast::NodePatternMatchMapping::static_type().to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("keys", keys.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("patterns", patterns.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("rest", rest.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
keys: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "keys", "MatchMapping")?,
|
||||
)?,
|
||||
patterns: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "patterns", "MatchMapping")?,
|
||||
)?,
|
||||
rest: get_node_field_opt(_vm, &_object, "rest")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
range: range_from_object(_vm, source_code, _object, "MatchMapping")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::PatternMatchClass {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
cls,
|
||||
arguments,
|
||||
range: _range,
|
||||
} = self;
|
||||
let (patterns, kwd_attrs, kwd_patterns) = split_pattern_match_class(arguments);
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodePatternMatchClass::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("cls", cls.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item("patterns", patterns.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item("kwd_attrs", kwd_attrs.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"kwd_patterns",
|
||||
kwd_patterns.ast_to_object(vm, source_code),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let patterns = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "patterns", "MatchClass")?,
|
||||
)?;
|
||||
let kwd_attrs = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "kwd_attrs", "MatchClass")?,
|
||||
)?;
|
||||
let kwd_patterns = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "kwd_patterns", "MatchClass")?,
|
||||
)?;
|
||||
let (patterns, keywords) = merge_pattern_match_class(patterns, kwd_attrs, kwd_patterns);
|
||||
|
||||
Ok(Self {
|
||||
cls: Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "cls", "MatchClass")?,
|
||||
)?,
|
||||
range: range_from_object(vm, source_code, object, "MatchClass")?,
|
||||
arguments: ruff::PatternArguments {
|
||||
range: Default::default(),
|
||||
patterns,
|
||||
keywords,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct PatternMatchClassPatterns {
|
||||
pub _range: TextRange, // TODO: Use this
|
||||
}
|
||||
|
||||
impl Node for PatternMatchClassPatterns {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct PatternMatchClassKeywordAttributes {
|
||||
pub _range: TextRange, // TODO: Use this
|
||||
}
|
||||
|
||||
impl Node for PatternMatchClassKeywordAttributes {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct PatternMatchClassKeywordPatterns {
|
||||
pub _range: TextRange, // TODO: Use this
|
||||
}
|
||||
|
||||
impl Node for PatternMatchClassKeywordPatterns {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::PatternMatchStar {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
name,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodePatternMatchStar::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("name", name.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
name: get_node_field_opt(_vm, &_object, "name")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
range: range_from_object(_vm, source_code, _object, "MatchStar")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::PatternMatchAs {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
pattern,
|
||||
name,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodePatternMatchAs::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("pattern", pattern.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("name", name.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
pattern: get_node_field_opt(_vm, &_object, "pattern")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
name: get_node_field_opt(_vm, &_object, "name")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
range: range_from_object(_vm, source_code, _object, "MatchAs")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::PatternMatchOr {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
patterns,
|
||||
range: _range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodePatternMatchOr::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("patterns", patterns.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
patterns: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "patterns", "MatchOr")?,
|
||||
)?,
|
||||
range: range_from_object(_vm, source_code, _object, "MatchOr")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn split_pattern_match_class(
|
||||
_arguments: ruff::PatternArguments,
|
||||
) -> (
|
||||
PatternMatchClassPatterns,
|
||||
PatternMatchClassKeywordAttributes,
|
||||
PatternMatchClassKeywordPatterns,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Merges the pattern match class attributes and patterns, opposite of [`split_pattern_match_class`].
|
||||
fn merge_pattern_match_class(
|
||||
_patterns: PatternMatchClassPatterns,
|
||||
_kwd_attrs: PatternMatchClassKeywordAttributes,
|
||||
_kwd_patterns: PatternMatchClassKeywordPatterns,
|
||||
) -> (Vec<ruff::Pattern>, Vec<ruff::PatternKeyword>) {
|
||||
todo!()
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,57 @@
|
||||
use super::PY_COMPILE_FLAG_AST_ONLY;
|
||||
|
||||
#[pymodule]
|
||||
pub(crate) mod _ast {
|
||||
use crate::{
|
||||
AsObject, Context, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{PyStrRef, PyTupleRef},
|
||||
function::FuncArgs,
|
||||
};
|
||||
#[pyattr]
|
||||
#[pyclass(module = "_ast", name = "AST")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(crate) struct NodeAst;
|
||||
|
||||
#[pyclass(flags(BASETYPE, HAS_DICT))]
|
||||
impl NodeAst {
|
||||
#[pyslot]
|
||||
#[pymethod(magic)]
|
||||
fn init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let fields = zelf.get_attr("_fields", vm)?;
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
let numargs = args.args.len();
|
||||
if numargs > fields.len() {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{} constructor takes at most {} positional argument{}",
|
||||
zelf.class().name(),
|
||||
fields.len(),
|
||||
if fields.len() == 1 { "" } else { "s" },
|
||||
)));
|
||||
}
|
||||
for (name, arg) in fields.iter().zip(args.args) {
|
||||
zelf.set_attr(name, arg, vm)?;
|
||||
}
|
||||
for (key, value) in args.kwargs {
|
||||
if let Some(pos) = fields.iter().position(|f| f.as_str() == key) {
|
||||
if pos < numargs {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{} got multiple values for argument '{}'",
|
||||
zelf.class().name(),
|
||||
key
|
||||
)));
|
||||
}
|
||||
}
|
||||
zelf.set_attr(vm.ctx.intern_str(key), value, vm)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pyattr(name = "_fields")]
|
||||
fn fields(ctx: &Context) -> PyTupleRef {
|
||||
ctx.empty_tuple.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr(name = "PyCF_ONLY_AST")]
|
||||
use super::PY_COMPILE_FLAG_AST_ONLY;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,354 @@
|
||||
use super::constant::{Constant, ConstantLiteral};
|
||||
use super::*;
|
||||
|
||||
fn ruff_fstring_value_into_iter(
|
||||
mut fstring_value: ruff::FStringValue,
|
||||
) -> impl Iterator<Item = ruff::FStringPart> + 'static {
|
||||
let default = ruff::FStringPart::FString(ruff::FString {
|
||||
range: Default::default(),
|
||||
elements: Default::default(),
|
||||
flags: Default::default(),
|
||||
});
|
||||
(0..fstring_value.as_slice().len()).map(move |i| {
|
||||
let fstring_value = &mut fstring_value;
|
||||
let tmp = fstring_value.into_iter().nth(i).unwrap();
|
||||
std::mem::replace(tmp, default.clone())
|
||||
})
|
||||
}
|
||||
|
||||
fn ruff_fstring_element_into_iter(
|
||||
mut fstring_element: ruff::FStringElements,
|
||||
) -> impl Iterator<Item = ruff::FStringElement> + 'static {
|
||||
let default = ruff::FStringElement::Literal(ruff::FStringLiteralElement {
|
||||
range: Default::default(),
|
||||
value: Default::default(),
|
||||
});
|
||||
(0..fstring_element.into_iter().len()).map(move |i| {
|
||||
let fstring_element = &mut fstring_element;
|
||||
let tmp = fstring_element.into_iter().nth(i).unwrap();
|
||||
std::mem::replace(tmp, default.clone())
|
||||
})
|
||||
}
|
||||
|
||||
fn fstring_part_to_joined_str_part(fstring_part: ruff::FStringPart) -> Vec<JoinedStrPart> {
|
||||
match fstring_part {
|
||||
ruff::FStringPart::Literal(ruff::StringLiteral {
|
||||
range,
|
||||
value,
|
||||
flags,
|
||||
}) => {
|
||||
vec![JoinedStrPart::Constant(Constant::new_str(
|
||||
value,
|
||||
flags.prefix(),
|
||||
range,
|
||||
))]
|
||||
}
|
||||
ruff::FStringPart::FString(ruff::FString {
|
||||
range: _,
|
||||
elements,
|
||||
flags: _, // TODO
|
||||
}) => ruff_fstring_element_into_iter(elements)
|
||||
.map(ruff_fstring_element_to_joined_str_part)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ruff_fstring_element_to_joined_str_part(element: ruff::FStringElement) -> JoinedStrPart {
|
||||
match element {
|
||||
ruff::FStringElement::Literal(ruff::FStringLiteralElement { range, value }) => {
|
||||
JoinedStrPart::Constant(Constant::new_str(
|
||||
value,
|
||||
ruff::str_prefix::StringLiteralPrefix::Empty,
|
||||
range,
|
||||
))
|
||||
}
|
||||
ruff::FStringElement::Expression(ruff::FStringExpressionElement {
|
||||
range,
|
||||
expression,
|
||||
debug_text: _, // TODO: What is this?
|
||||
conversion,
|
||||
format_spec,
|
||||
}) => JoinedStrPart::FormattedValue(FormattedValue {
|
||||
value: expression,
|
||||
conversion,
|
||||
format_spec: ruff_format_spec_to_joined_str(format_spec),
|
||||
range,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn ruff_format_spec_to_joined_str(
|
||||
format_spec: Option<Box<ruff::FStringFormatSpec>>,
|
||||
) -> Option<Box<JoinedStr>> {
|
||||
match format_spec {
|
||||
None => None,
|
||||
Some(format_spec) => {
|
||||
let ruff::FStringFormatSpec { range, elements } = *format_spec;
|
||||
let values: Vec<_> = ruff_fstring_element_into_iter(elements)
|
||||
.map(ruff_fstring_element_to_joined_str_part)
|
||||
.collect();
|
||||
let values = values.into_boxed_slice();
|
||||
Some(Box::new(JoinedStr { values, range }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ruff_fstring_element_to_ruff_fstring_part(element: ruff::FStringElement) -> ruff::FStringPart {
|
||||
match element {
|
||||
ruff::FStringElement::Literal(value) => {
|
||||
let ruff::FStringLiteralElement { range, value } = value;
|
||||
ruff::FStringPart::Literal(ruff::StringLiteral {
|
||||
range,
|
||||
value,
|
||||
flags: Default::default(),
|
||||
})
|
||||
}
|
||||
ruff::FStringElement::Expression(value) => {
|
||||
let ruff::FStringExpressionElement {
|
||||
range,
|
||||
expression,
|
||||
debug_text,
|
||||
conversion,
|
||||
format_spec,
|
||||
} = value;
|
||||
ruff::FStringPart::FString(ruff::FString {
|
||||
range,
|
||||
elements: vec![ruff::FStringElement::Expression(
|
||||
ruff::FStringExpressionElement {
|
||||
range,
|
||||
expression,
|
||||
debug_text,
|
||||
conversion,
|
||||
format_spec,
|
||||
},
|
||||
)]
|
||||
.into(),
|
||||
flags: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn joined_str_to_ruff_format_spec(
|
||||
joined_str: Option<Box<JoinedStr>>,
|
||||
) -> Option<Box<ruff::FStringFormatSpec>> {
|
||||
match joined_str {
|
||||
None => None,
|
||||
Some(joined_str) => {
|
||||
let JoinedStr { range, values } = *joined_str;
|
||||
let elements: Vec<_> = Box::into_iter(values)
|
||||
.map(joined_str_part_to_ruff_fstring_element)
|
||||
.collect();
|
||||
let format_spec = ruff::FStringFormatSpec {
|
||||
range,
|
||||
elements: elements.into(),
|
||||
};
|
||||
Some(Box::new(format_spec))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct JoinedStr {
|
||||
pub(super) range: TextRange,
|
||||
pub(super) values: Box<[JoinedStrPart]>,
|
||||
}
|
||||
|
||||
impl JoinedStr {
|
||||
pub(super) fn into_expr(self) -> ruff::Expr {
|
||||
let Self { range, values } = self;
|
||||
ruff::Expr::FString(ruff::ExprFString {
|
||||
range: Default::default(),
|
||||
value: match values.len() {
|
||||
// ruff represents an empty fstring like this:
|
||||
0 => ruff::FStringValue::single(ruff::FString {
|
||||
range,
|
||||
elements: vec![].into(),
|
||||
flags: Default::default(),
|
||||
}),
|
||||
1 => ruff::FStringValue::single(
|
||||
Box::<[_]>::into_iter(values)
|
||||
.map(joined_str_part_to_ruff_fstring_element)
|
||||
.map(|element| ruff::FString {
|
||||
range,
|
||||
elements: vec![element].into(),
|
||||
flags: Default::default(),
|
||||
})
|
||||
.next()
|
||||
.expect("FString has exactly one part"),
|
||||
),
|
||||
_ => ruff::FStringValue::concatenated(
|
||||
Box::<[_]>::into_iter(values)
|
||||
.map(joined_str_part_to_ruff_fstring_element)
|
||||
.map(ruff_fstring_element_to_ruff_fstring_part)
|
||||
.collect(),
|
||||
),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn joined_str_part_to_ruff_fstring_element(part: JoinedStrPart) -> ruff::FStringElement {
|
||||
match part {
|
||||
JoinedStrPart::FormattedValue(value) => {
|
||||
ruff::FStringElement::Expression(ruff::FStringExpressionElement {
|
||||
range: value.range,
|
||||
expression: value.value.clone(),
|
||||
debug_text: None, // TODO: What is this?
|
||||
conversion: value.conversion,
|
||||
format_spec: joined_str_to_ruff_format_spec(value.format_spec),
|
||||
})
|
||||
}
|
||||
JoinedStrPart::Constant(value) => {
|
||||
ruff::FStringElement::Literal(ruff::FStringLiteralElement {
|
||||
range: value.range,
|
||||
value: match value.value {
|
||||
ConstantLiteral::Str { value, .. } => value,
|
||||
_ => todo!(),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// constructor
|
||||
impl Node for JoinedStr {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self { values, range } = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeExprJoinedStr::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item(
|
||||
"values",
|
||||
BoxedSlice(values).ast_to_object(vm, source_code),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let values: BoxedSlice<_> = Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "values", "JoinedStr")?,
|
||||
)?;
|
||||
Ok(Self {
|
||||
values: values.0,
|
||||
range: range_from_object(vm, source_code, object, "JoinedStr")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum JoinedStrPart {
|
||||
FormattedValue(FormattedValue),
|
||||
Constant(Constant),
|
||||
}
|
||||
|
||||
// constructor
|
||||
impl Node for JoinedStrPart {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
match self {
|
||||
JoinedStrPart::FormattedValue(value) => value.ast_to_object(vm, source_code),
|
||||
JoinedStrPart::Constant(value) => value.ast_to_object(vm, source_code),
|
||||
}
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let cls = object.class();
|
||||
if cls.is(pyast::NodeExprFormattedValue::static_type()) {
|
||||
Ok(Self::FormattedValue(Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
object,
|
||||
)?))
|
||||
} else {
|
||||
Ok(Self::Constant(Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
object,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct FormattedValue {
|
||||
value: Box<ruff::Expr>,
|
||||
conversion: ruff::ConversionFlag,
|
||||
format_spec: Option<Box<JoinedStr>>,
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
// constructor
|
||||
impl Node for FormattedValue {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
value,
|
||||
conversion,
|
||||
format_spec,
|
||||
range,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeExprFormattedValue::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("value", value.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item("conversion", conversion.ast_to_object(vm, source_code), vm)
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"format_spec",
|
||||
format_spec.ast_to_object(vm, source_code),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
value: Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "value", "FormattedValue")?,
|
||||
)?,
|
||||
conversion: Node::ast_from_object(
|
||||
vm,
|
||||
source_code,
|
||||
get_node_field(vm, &object, "conversion", "FormattedValue")?,
|
||||
)?,
|
||||
format_spec: get_node_field_opt(vm, &object, "format_spec")?
|
||||
.map(|obj| Node::ast_from_object(vm, source_code, obj))
|
||||
.transpose()?,
|
||||
range: range_from_object(vm, source_code, object, "FormattedValue")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn fstring_to_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
expression: ruff::ExprFString,
|
||||
) -> PyObjectRef {
|
||||
let ruff::ExprFString { range, value } = expression;
|
||||
let values: Vec<_> = ruff_fstring_value_into_iter(value)
|
||||
.flat_map(fstring_part_to_joined_str_part)
|
||||
.collect();
|
||||
let values = values.into_boxed_slice();
|
||||
let c = JoinedStr { range, values };
|
||||
c.ast_to_object(vm, source_code)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) enum TypeIgnore {
|
||||
TypeIgnore(TypeIgnoreTypeIgnore),
|
||||
}
|
||||
|
||||
// sum
|
||||
impl Node for TypeIgnore {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
match self {
|
||||
TypeIgnore::TypeIgnore(cons) => cons.ast_to_object(vm, source_code),
|
||||
}
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let _cls = _object.class();
|
||||
Ok(if _cls.is(pyast::NodeTypeIgnoreTypeIgnore::static_type()) {
|
||||
TypeIgnore::TypeIgnore(TypeIgnoreTypeIgnore::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else {
|
||||
return Err(_vm.new_type_error(format!(
|
||||
"expected some sort of type_ignore, but got {}",
|
||||
_object.repr(_vm)?
|
||||
)));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct TypeIgnoreTypeIgnore {
|
||||
range: TextRange,
|
||||
lineno: PyRefExact<PyInt>,
|
||||
tag: PyRefExact<PyStr>,
|
||||
}
|
||||
|
||||
// constructor
|
||||
impl Node for TypeIgnoreTypeIgnore {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self { lineno, tag, range } = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(
|
||||
vm,
|
||||
pyast::NodeTypeIgnoreTypeIgnore::static_type().to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("lineno", lineno.to_pyobject(vm), vm).unwrap();
|
||||
dict.set_item("tag", tag.to_pyobject(vm), vm).unwrap();
|
||||
node_add_location(&dict, range, vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
lineno: get_node_field(vm, &object, "lineno", "TypeIgnore")?
|
||||
.downcast_exact(vm)
|
||||
.unwrap(),
|
||||
tag: get_node_field(vm, &object, "tag", "TypeIgnore")?
|
||||
.downcast_exact(vm)
|
||||
.unwrap(),
|
||||
range: range_from_object(vm, source_code, object, "TypeIgnore")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
use super::*;
|
||||
|
||||
impl Node for ruff::TypeParams {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
self.type_params.ast_to_object(vm, source_code)
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
_source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let type_params: Vec<ruff::TypeParam> = Node::ast_from_object(_vm, _source_code, _object)?;
|
||||
let range = Option::zip(type_params.first(), type_params.last())
|
||||
.map(|(first, last)| first.range().cover(last.range()))
|
||||
.unwrap_or_default();
|
||||
Ok(Self { type_params, range })
|
||||
}
|
||||
|
||||
fn is_none(&self) -> bool {
|
||||
self.type_params.is_empty()
|
||||
}
|
||||
}
|
||||
// sum
|
||||
impl Node for ruff::TypeParam {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
match self {
|
||||
Self::TypeVar(cons) => cons.ast_to_object(vm, source_code),
|
||||
Self::ParamSpec(cons) => cons.ast_to_object(vm, source_code),
|
||||
Self::TypeVarTuple(cons) => cons.ast_to_object(vm, source_code),
|
||||
}
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let _cls = _object.class();
|
||||
Ok(if _cls.is(pyast::NodeTypeParamTypeVar::static_type()) {
|
||||
ruff::TypeParam::TypeVar(ruff::TypeParamTypeVar::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else if _cls.is(pyast::NodeTypeParamParamSpec::static_type()) {
|
||||
ruff::TypeParam::ParamSpec(ruff::TypeParamParamSpec::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else if _cls.is(pyast::NodeTypeParamTypeVarTuple::static_type()) {
|
||||
ruff::TypeParam::TypeVarTuple(ruff::TypeParamTypeVarTuple::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
_object,
|
||||
)?)
|
||||
} else {
|
||||
return Err(_vm.new_type_error(format!(
|
||||
"expected some sort of type_param, but got {}",
|
||||
_object.repr(_vm)?
|
||||
)));
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::TypeParamTypeVar {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
name,
|
||||
bound,
|
||||
range: _range,
|
||||
default: _,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodeTypeParamTypeVar::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("name", name.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("bound", bound.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
name: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "name", "TypeVar")?,
|
||||
)?,
|
||||
bound: get_node_field_opt(_vm, &_object, "bound")?
|
||||
.map(|obj| Node::ast_from_object(_vm, source_code, obj))
|
||||
.transpose()?,
|
||||
default: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "default_value", "TypeVar")?,
|
||||
)?,
|
||||
range: range_from_object(_vm, source_code, _object, "TypeVar")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::TypeParamParamSpec {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
name,
|
||||
range: _range,
|
||||
default,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodeTypeParamParamSpec::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("name", name.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"default_value",
|
||||
default.ast_to_object(_vm, source_code),
|
||||
_vm,
|
||||
)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
name: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "name", "ParamSpec")?,
|
||||
)?,
|
||||
default: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "default_value", "ParamSpec")?,
|
||||
)?,
|
||||
range: range_from_object(_vm, source_code, _object, "ParamSpec")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
// constructor
|
||||
impl Node for ruff::TypeParamTypeVarTuple {
|
||||
fn ast_to_object(self, _vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
let Self {
|
||||
name,
|
||||
range: _range,
|
||||
default,
|
||||
} = self;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(
|
||||
_vm,
|
||||
pyast::NodeTypeParamTypeVarTuple::static_type().to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("name", name.ast_to_object(_vm, source_code), _vm)
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"default_value",
|
||||
default.ast_to_object(_vm, source_code),
|
||||
_vm,
|
||||
)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_code);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
_vm: &VirtualMachine,
|
||||
source_code: &SourceCodeOwned,
|
||||
_object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
name: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "name", "TypeVarTuple")?,
|
||||
)?,
|
||||
default: Node::ast_from_object(
|
||||
_vm,
|
||||
source_code,
|
||||
get_node_field(_vm, &_object, "default_value", "TypeVarTuple")?,
|
||||
)?,
|
||||
range: range_from_object(_vm, source_code, _object, "TypeVarTuple")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -110,10 +110,15 @@ mod builtins {
|
||||
_feature_version: OptionalArg<i32>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "rustpython-parser", feature = "rustpython-codegen"))]
|
||||
#[cfg(any(feature = "parser", feature = "compiler"))]
|
||||
#[pyfunction]
|
||||
fn compile(args: CompileArgs, vm: &VirtualMachine) -> PyResult {
|
||||
#[cfg(feature = "rustpython-ast")]
|
||||
#[cfg(not(feature = "ast"))]
|
||||
{
|
||||
_ = args; // to disable unused warning
|
||||
return Err(vm.new_type_error("AST Not Supported".to_owned()));
|
||||
}
|
||||
#[cfg(feature = "ast")]
|
||||
{
|
||||
use crate::{class::PyClassImpl, stdlib::ast};
|
||||
|
||||
@@ -155,16 +160,16 @@ mod builtins {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rustpython-parser"))]
|
||||
#[cfg(not(feature = "parser"))]
|
||||
{
|
||||
const PARSER_NOT_SUPPORTED: &str = "can't compile() source code when the `parser` feature of rustpython is disabled";
|
||||
Err(vm.new_type_error(PARSER_NOT_SUPPORTED.to_owned()))
|
||||
}
|
||||
#[cfg(feature = "rustpython-parser")]
|
||||
#[cfg(feature = "parser")]
|
||||
{
|
||||
use crate::convert::ToPyException;
|
||||
use num_traits::Zero;
|
||||
use rustpython_parser as parser;
|
||||
use ruff_python_parser as parser;
|
||||
|
||||
let source = ArgStrOrBytesLike::try_from_object(vm, args.source)?;
|
||||
let source = source.borrow_bytes();
|
||||
@@ -180,11 +185,11 @@ mod builtins {
|
||||
}
|
||||
|
||||
if (flags & ast::PY_COMPILE_FLAG_AST_ONLY).is_zero() {
|
||||
#[cfg(not(feature = "rustpython-compiler"))]
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
{
|
||||
Err(vm.new_value_error(CODEGEN_NOT_SUPPORTED.to_owned()))
|
||||
}
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
#[cfg(feature = "compiler")]
|
||||
{
|
||||
let mode = mode_str
|
||||
.parse::<crate::compiler::Mode>()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#[cfg(feature = "rustpython-ast")]
|
||||
#[cfg(feature = "ast")]
|
||||
pub(crate) mod ast;
|
||||
pub mod atexit;
|
||||
pub mod builtins;
|
||||
@@ -15,7 +15,7 @@ mod operator;
|
||||
// mod re;
|
||||
mod sre;
|
||||
mod string;
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
#[cfg(feature = "compiler")]
|
||||
mod symtable;
|
||||
mod sysconfigdata;
|
||||
#[cfg(feature = "threading")]
|
||||
@@ -98,12 +98,12 @@ pub fn get_module_inits() -> StdlibMap {
|
||||
sys::sysconfigdata_name() => sysconfigdata::make_module,
|
||||
}
|
||||
// parser related modules:
|
||||
#[cfg(feature = "rustpython-ast")]
|
||||
#[cfg(feature = "ast")]
|
||||
{
|
||||
"_ast" => ast::make_module,
|
||||
}
|
||||
// compiler related modules:
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
#[cfg(feature = "compiler")]
|
||||
{
|
||||
"symtable" => symtable::make_module,
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ impl VirtualMachine {
|
||||
source_path: String,
|
||||
opts: CompileOpts,
|
||||
) -> Result<PyRef<PyCode>, CompileError> {
|
||||
compiler::compile(source, mode, source_path, opts).map(|code| self.ctx.new_code(code))
|
||||
compiler::compile(source, mode, &source_path, opts).map(|code| self.ctx.new_code(code))
|
||||
}
|
||||
|
||||
pub fn run_script(&self, scope: Scope, path: &str) -> PyResult<()> {
|
||||
|
||||
+45
-18
@@ -256,22 +256,39 @@ impl VirtualMachine {
|
||||
self.new_exception_msg(overflow_error, msg)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "rustpython-parser", feature = "rustpython-codegen"))]
|
||||
#[cfg(any(feature = "parser", feature = "compiler"))]
|
||||
pub fn new_syntax_error(
|
||||
&self,
|
||||
error: &crate::compiler::CompileError,
|
||||
source: Option<&str>,
|
||||
) -> PyBaseExceptionRef {
|
||||
use crate::source_code::SourceLocation;
|
||||
use crate::source::SourceLocation;
|
||||
|
||||
let syntax_error_type = match &error.error {
|
||||
#[cfg(feature = "rustpython-parser")]
|
||||
crate::compiler::CompileErrorType::Parse(p) if p.is_indentation_error() => {
|
||||
self.ctx.exceptions.indentation_error
|
||||
}
|
||||
#[cfg(feature = "rustpython-parser")]
|
||||
crate::compiler::CompileErrorType::Parse(p) if p.is_tab_error() => {
|
||||
self.ctx.exceptions.tab_error
|
||||
let syntax_error_type = match &error {
|
||||
#[cfg(feature = "parser")]
|
||||
// FIXME: this condition will cause TabError even when the matching actual error is IndentationError
|
||||
crate::compiler::CompileError::Parse(rustpython_compiler::ParseError {
|
||||
error:
|
||||
ruff_python_parser::ParseErrorType::Lexical(
|
||||
ruff_python_parser::lexer::LexicalErrorType::IndentationError,
|
||||
),
|
||||
..
|
||||
}) => self.ctx.exceptions.tab_error,
|
||||
#[cfg(feature = "parser")]
|
||||
crate::compiler::CompileError::Parse(rustpython_compiler::ParseError {
|
||||
error: ruff_python_parser::ParseErrorType::UnexpectedIndentation,
|
||||
..
|
||||
}) => self.ctx.exceptions.indentation_error,
|
||||
#[cfg(feature = "parser")]
|
||||
crate::compiler::CompileError::Parse(rustpython_compiler::ParseError {
|
||||
error: ruff_python_parser::ParseErrorType::OtherError(s),
|
||||
..
|
||||
}) => {
|
||||
if s.starts_with("Expected an indented block after") {
|
||||
self.ctx.exceptions.indentation_error
|
||||
} else {
|
||||
self.ctx.exceptions.syntax_error
|
||||
}
|
||||
}
|
||||
_ => self.ctx.exceptions.syntax_error,
|
||||
}
|
||||
@@ -281,18 +298,32 @@ impl VirtualMachine {
|
||||
fn get_statement(source: &str, loc: Option<SourceLocation>) -> Option<String> {
|
||||
let line = source
|
||||
.split('\n')
|
||||
.nth(loc?.row.to_zero_indexed_usize())?
|
||||
.nth(loc?.row.to_zero_indexed())?
|
||||
.to_owned();
|
||||
Some(line + "\n")
|
||||
}
|
||||
|
||||
let statement = if let Some(source) = source {
|
||||
get_statement(source, error.location)
|
||||
get_statement(source, error.location())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let syntax_error = self.new_exception_msg(syntax_error_type, error.error.to_string());
|
||||
let mut msg = error.to_string();
|
||||
if let Some(msg) = msg.get_mut(..1) {
|
||||
msg.make_ascii_lowercase();
|
||||
}
|
||||
match error {
|
||||
#[cfg(feature = "parser")]
|
||||
crate::compiler::CompileError::Parse(rustpython_compiler::ParseError {
|
||||
error:
|
||||
ruff_python_parser::ParseErrorType::FStringError(_)
|
||||
| ruff_python_parser::ParseErrorType::UnexpectedExpressionToken,
|
||||
..
|
||||
}) => msg.insert_str(0, "invalid syntax: "),
|
||||
_ => {}
|
||||
}
|
||||
let syntax_error = self.new_exception_msg(syntax_error_type, msg);
|
||||
let (lineno, offset) = error.python_location();
|
||||
let lineno = self.ctx.new_int(lineno);
|
||||
let offset = self.ctx.new_int(offset);
|
||||
@@ -311,11 +342,7 @@ impl VirtualMachine {
|
||||
.unwrap();
|
||||
syntax_error
|
||||
.as_object()
|
||||
.set_attr(
|
||||
"filename",
|
||||
self.ctx.new_str(error.source_path.clone()),
|
||||
self,
|
||||
)
|
||||
.set_attr("filename", self.ctx.new_str(error.source_path()), self)
|
||||
.unwrap();
|
||||
syntax_error
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ rustpython-stdlib = { workspace = true, default-features = false, optional = tru
|
||||
# make sure no threading! otherwise wasm build will fail
|
||||
rustpython-vm = { workspace = true, features = ["compiler", "encodings", "serde", "wasmbind"] }
|
||||
|
||||
rustpython-parser = { workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
|
||||
serde = { workspace = true }
|
||||
wasm-bindgen = { workspace = true }
|
||||
|
||||
+10
-11
@@ -3,11 +3,10 @@
|
||||
use crate::js_module;
|
||||
use crate::vm_class::{WASMVirtualMachine, stored_vm_from_wasm};
|
||||
use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, SyntaxError, Uint8Array};
|
||||
use rustpython_parser::{ParseErrorType, lexer::LexicalErrorType};
|
||||
use rustpython_vm::{
|
||||
AsObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject, VirtualMachine,
|
||||
builtins::PyBaseExceptionRef,
|
||||
compiler::{CompileError, CompileErrorType},
|
||||
compiler::{CompileError, ParseError, parser::ParseErrorType, parser::lexer::LexicalErrorType},
|
||||
exceptions,
|
||||
function::{ArgBytesLike, FuncArgs},
|
||||
py_serde,
|
||||
@@ -252,21 +251,21 @@ pub fn syntax_err(err: CompileError) -> SyntaxError {
|
||||
let _ = Reflect::set(
|
||||
&js_err,
|
||||
&"row".into(),
|
||||
&(err.location.unwrap().row.get()).into(),
|
||||
&(err.location().unwrap().row.get()).into(),
|
||||
);
|
||||
let _ = Reflect::set(
|
||||
&js_err,
|
||||
&"col".into(),
|
||||
&(err.location.unwrap().column.get()).into(),
|
||||
&(err.location().unwrap().column.get()).into(),
|
||||
);
|
||||
// | ParseErrorType::UnrecognizedToken(Token::Dedent, _)
|
||||
let can_continue = matches!(
|
||||
&err.error,
|
||||
CompileErrorType::Parse(
|
||||
ParseErrorType::Eof
|
||||
| ParseErrorType::Lexical(LexicalErrorType::Eof)
|
||||
| ParseErrorType::Lexical(LexicalErrorType::IndentationError)
|
||||
| ParseErrorType::UnrecognizedToken(rustpython_parser::Tok::Dedent, _)
|
||||
)
|
||||
&err,
|
||||
CompileError::Parse(ParseError {
|
||||
error: ParseErrorType::Lexical(LexicalErrorType::Eof)
|
||||
| ParseErrorType::Lexical(LexicalErrorType::IndentationError),
|
||||
..
|
||||
})
|
||||
);
|
||||
let _ = Reflect::set(&js_err, &"canContinue".into(), &can_continue.into());
|
||||
js_err
|
||||
|
||||
Reference in new issue
Block a user