diff --git a/.cspell.json b/.cspell.json deleted file mode 100644 index 7c283118..00000000 --- a/.cspell.json +++ /dev/null @@ -1,304 +0,0 @@ -// See: https://github.com/streetsidesoftware/cspell/tree/master/packages/cspell -{ - "version": "0.2", - // language - current active spelling language - "language": "en", - // dictionaries - list of the names of the dictionaries to use - "dictionaries": [ - "en_US", - "softwareTerms", - "c", - "cpp", - "python", - "python-custom", - "rust", - "unix", - "posix", - "winapi" - ], - // dictionaryDefinitions - this list defines any custom dictionaries to use - "dictionaryDefinitions": [], - "ignorePaths": [ - "**/__pycache__/**", - "Lib/**" - ], - // words - list of words to be always considered correct - "words": [ - // Rust - "ahash", - "bidi", - "biguint", - "bindgen", - "bitflags", - "bstr", - "byteorder", - "chrono", - "consts", - "cstring", - "flate2", - "fract", - "hasher", - "hexf", - "idents", - "indexmap", - "insta", - "keccak", - "lalrpop", - "libc", - "libz", - "longlong", - "Manually", - "nbaz", - "maplit", - "memchr", - "memrchr", - "memmap", - "metas", - "modpow", - "nanos", - "peekable", - "powc", - "powf", - "prepended", - "punct", - "replacen", - "rsplitn", - "rustc", - "rustfmt", - "seekfrom", - "splitn", - "subsec", - "timsort", - "trai", - "ulonglong", - "unic", - "unistd", - "unsync", - "winapi", - "winsock", - // Python - "abstractmethods", - "aiter", - "anext", - "arrayiterator", - "arraytype", - "asend", - "athrow", - "basicsize", - "cformat", - "classcell", - "closesocket", - "codepoint", - "codepoints", - "cpython", - "decompressor", - "defaultaction", - "descr", - "dictcomp", - "dictitems", - "dictkeys", - "dictview", - "docstring", - "docstrings", - "dunder", - "eventmask", - "fdel", - "fget", - "fileencoding", - "fillchar", - "finallyhandler", - "frombytes", - "fromhex", - "fromunicode", - "fset", - "fspath", - "fstring", - "fstrings", - "genexpr", - "getattro", - "getformat", - "getnewargs", - "getweakrefcount", - "getweakrefs", - "hostnames", - "idiv", - "impls", - "infj", - "instancecheck", - "instanceof", - "isabstractmethod", - "itemiterator", - "itemsize", - "iternext", - "keyiterator", - "kwarg", - "kwargs", - "linearization", - "linearize", - "listcomp", - "mappingproxy", - "maxsplit", - "memoryview", - "memoryviewiterator", - "metaclass", - "metaclasses", - "metatype", - "mro", - "mros", - "nanj", - "ndigits", - "ndim", - "nonbytes", - "origname", - "posixsubprocess", - "pyexpat", - "PYTHONDEBUG", - "PYTHONHOME", - "PYTHONINSPECT", - "PYTHONOPTIMIZE", - "PYTHONPATH", - "PYTHONPATH", - "PYTHONVERBOSE", - "PYTHONWARNINGS", - "qualname", - "radd", - "rdiv", - "rdivmod", - "reconstructor", - "reversevalueiterator", - "rfloordiv", - "rlshift", - "rmod", - "rpow", - "rrshift", - "rsub", - "rtruediv", - "scproxy", - "setattro", - "setcomp", - "stacklevel", - "subclasscheck", - "subclasshook", - "unionable", - "unraisablehook", - "valueiterator", - "vararg", - "varargs", - "varnames", - "warningregistry", - "warnopts", - "weakproxy", - "xopts", - // RustPython - "baseclass", - "Bytecode", - "cfgs", - "codegen", - "dedentations", - "dedents", - "deduped", - "downcasted", - "dumpable", - "GetSet", - "internable", - "makeunicodedata", - "miri", - "nonterminal", - "notrace", - "pyarg", - "pyarg", - "pyargs", - "PyAttr", - "pyc", - "PyClass", - "PyClassMethod", - "PyException", - "PyFunction", - "pygetset", - "pyimpl", - "pymember", - "PyMethod", - "PyModule", - "pyname", - "pyobj", - "PyObject", - "pypayload", - "PyProperty", - "pyref", - "PyResult", - "pyslot", - "PyStaticMethod", - "pystr", - "pystruct", - "pystructseq", - "pytrace", - "reducelib", - "richcompare", - "RustPython", - "struc", - "tracebacks", - "typealiases", - "Unconstructible", - "unhashable", - "uninit", - "unraisable", - "wasi", - "zelf", - // cpython - "argtypes", - "asdl", - "asname", - "augassign", - "badsyntax", - "basetype", - "boolop", - "bxor", - "cellarg", - "cellvar", - "cellvars", - "cmpop", - "dictoffset", - "elts", - "excepthandler", - "finalbody", - "freevar", - "freevars", - "fromlist", - "heaptype", - "IMMUTABLETYPE", - "kwonlyarg", - "kwonlyargs", - "linearise", - "maxdepth", - "mult", - "nkwargs", - "orelse", - "patma", - "posonlyarg", - "posonlyargs", - "prec", - "significand", - "stackdepth", - "unaryop", - "unparse", - "unparser", - "VARKEYWORDS", - "varkwarg", - "wbits", - "withitem", - "withitems", - "withs" - ], - // flagWords - list of words to be always considered incorrect - "flagWords": [ - ], - "ignoreRegExpList": [ - ], - // languageSettings - allow for per programming language configuration settings. - "languageSettings": [ - { - "languageId": "python", - "locale": "en" - } - ] -} diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index e1a7c56f..00000000 --- a/.gitattributes +++ /dev/null @@ -1,5 +0,0 @@ -parser/src/python.rs linguist-generated -**/*.snap linguist-generated -merge -**/*.lalrpop text eol=LF -**/*.py text working-tree-encoding=UTF-8 eol=LF -**/*.rs text working-tree-encoding=UTF-8 eol=LF diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index 9d9358bd..00000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,76 +0,0 @@ -on: - push: - branches: [main, release] - pull_request: - types: [opened, synchronize, reopened] - merge_group: - -name: CI - -# Cancel previous workflows if they are the same workflow on same ref (branch/tags) -# with the same event (push/pull_request) even they are in progress. -# This setting will help reduce the number of duplicated workflows. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} - cancel-in-progress: true - -env: - CARGO_ARGS: --no-default-features --features stdlib,zlib,importlib,encodings,ssl,jit - -jobs: - rust_tests: - if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip:ci') }} - env: - RUST_BACKTRACE: full - name: Run rust tests - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - fail-fast: false - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - - name: Set up the Mac environment - run: brew install autoconf automake libtool - if: runner.os == 'macOS' - - - uses: Swatinem/rust-cache@v2 - - - name: run tests with num-bigint - run: cargo test --all --no-default-features --features num-bigint - - name: run tests with malachite-bigint and all features - run: cargo test --all --features location,malachite-bigint,constant-optimization,fold,unparse,visitor,all-nodes-with-ranges,full-lexer,serde --exclude rustpython-ast-pyo3 - - lint: - name: Check Rust code with rustfmt and clippy - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt, clippy - - name: run rustfmt - run: cargo fmt --all -- --check - - name: run clippy - run: cargo clippy --all --no-default-features --features num-bigint - - name: run clippy - run: cargo clippy --all --features location,malachite-bigint,constant-optimization,fold,unparse,visitor,all-nodes-with-ranges,full-lexer,serde --exclude rustpython-ast-pyo3 -- -Dwarnings - - - uses: actions/setup-python@v4 - with: - python-version: "3.11" - - name: install ruff - run: python -m pip install ruff - - name: run python lint - run: ruff check ast - - - name: spell checker - uses: streetsidesoftware/cspell-action@v2 - with: - files: | - 'ast/**/*.rs' - 'core/**/*.rs' - 'literal/**/*.rs' - 'parser/**/*.rs' - 'ast/asdl_rs.py' diff --git a/Cargo.toml b/Cargo.toml index a9811e90..5da28823 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [workspace.package] -version = "0.4.0" +version = "0.2.0" authors = ["RustPython Team"] edition = "2021" -rust-version = "1.72.1" +rust-version = "1.67.1" description = "Python parser and its dependencies." repository = "https://github.com/RustPython/Parser" license = "MIT" @@ -11,34 +11,48 @@ include = ["LICENSE", "Cargo.toml", "src/**/*.rs"] [workspace] resolver = "2" members = [ - "ast", "core", "format", "literal", "parser", "vendored", + "ast", "core", "literal", "parser", ] [workspace.dependencies] -rustpython-parser-vendored = { path = "vendored", version = "0.4.0" } -rustpython-ast = { path = "ast", default-features = false, version = "0.4.0" } -rustpython-parser-core = { path = "core", features = [], version = "0.4.0" } -rustpython-literal = { path = "literal", version = "0.4.0" } -rustpython-format = { path = "format", default-features = false, version = "0.4.0" } -rustpython-parser = { path = "parser", default-features = false, version = "0.4.0" } - +ahash = "0.7.6" anyhow = "1.0.45" -bitflags = "2.4.0" +ascii = "1.0" +atty = "0.2.14" +bincode = "1.3.3" +bitflags = "1.3.2" +bstr = "0.2.17" cfg-if = "1.0" +chrono = "0.4.19" +crossbeam-utils = "0.8.9" +flame = "0.2.2" +glob = "0.3" +hex = "0.4.3" +indexmap = "1.8.1" insta = "1.14.0" -itertools = "0.11.0" -is-macro = "0.3.0" +itertools = "0.10.3" +libc = "0.2.133" log = "0.4.16" +nix = "0.26" num-complex = "0.4.0" num-bigint = "0.4.3" +num-integer = "0.1.44" +num-rational = "0.4.0" num-traits = "0.2" -malachite-bigint = "0.2.3" -memchr = "2.5.0" +num_enum = "0.5.7" +once_cell = "1.13" +parking_lot = "0.12" +paste = "1.0.7" rand = "0.8.5" -serde = { version = "1.0.133", default-features = false } +rustyline = "11" +serde = "1.0" +schannel = "0.1.19" static_assertions = "1.1" -once_cell = "1.18.0" -unicode_names2 = "1.1.0" +syn = "1.0.91" +thiserror = "1.0" +thread_local = "1.1.4" +unicode_names2 = { version = "0.6.0", git = "https://github.com/youknowone/unicode_names2.git", rev = "4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde" } +widestring = "0.5.1" [profile.dev.package."*"] opt-level = 3 diff --git a/README.md b/README.md deleted file mode 100644 index fabc83b7..00000000 --- a/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# RustPython parser as a library - -This project is forked from [RustPython][RustPython] project. The parser is one of the core part of [RustPython] and [Ruff] project. - -We try to keep these crates as a well-packaged library for more potential users. - -## Projects - -- [RustPython][RustPython] is a Python interpreter -- [Ruff][Ruff] is an extremely fast Python linter -- [Pylyzer][Pylyzer] is a static code analyzer / language server for Python -- [Baembal][Baembal] is a Python package to accelerate `ast.parse` - -[RustPython]: https://github.com/RustPython/RustPython -[Ruff]: https://github.com/charliermarsh/ruff -[Pylyzer]: https://github.com/mtshiba/pylyzer -[Baembal]: https://github.com/youknowone/baembal diff --git a/ast/Cargo.toml b/ast/Cargo.toml index 53efc88c..5de211dd 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -1,27 +1,20 @@ [package] name = "rustpython-ast" +version = "0.2.0" description = "AST definitions for RustPython" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -repository = { workspace = true } -license = { workspace = true } -rust-version = { workspace = true } +authors = ["RustPython Team"] +edition = "2021" +repository = "https://github.com/RustPython/RustPython" +license = "MIT" [features] -default = ["location", "malachite-bigint"] +default = ["constant-optimization", "fold"] constant-optimization = ["fold"] -location = ["fold", "rustpython-parser-core/location"] fold = [] unparse = ["rustpython-literal"] -visitor = [] -all-nodes-with-ranges = [] [dependencies] -rustpython-parser-core = { workspace = true } -rustpython-literal = { workspace = true, optional = true } +rustpython-compiler-core = { path = "../core", version = "0.2.0" } +rustpython-literal = { path = "../literal", version = "0.2.0", optional = true } -is-macro = { workspace = true } -num-bigint = { workspace = true, optional = true } -malachite-bigint = { workspace = true, optional = true } -static_assertions = "1.1.0" +num-bigint = { workspace = true } diff --git a/ast/Python.asdl b/ast/Python.asdl index 0d154867..e9423a7c 100644 --- a/ast/Python.asdl +++ b/ast/Python.asdl @@ -10,22 +10,20 @@ module Python stmt = FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, - string? type_comment, type_param* type_params) + string? type_comment) | AsyncFunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, - string? type_comment, type_param* type_params) + string? type_comment) | ClassDef(identifier name, expr* bases, keyword* keywords, stmt* body, - expr* decorator_list, - type_param* type_params) + expr* decorator_list) | Return(expr? value) | Delete(expr* targets) | Assign(expr* targets, expr value, string? type_comment) - | TypeAlias(expr name, type_param* type_params, expr value) | AugAssign(expr target, operator op, expr value) -- 'simple' indicates that we annotate simple name without parens | AnnAssign(expr target, expr annotation, expr? value, int simple) @@ -144,9 +142,4 @@ module Python attributes (int lineno, int col_offset, int end_lineno, int end_col_offset) type_ignore = TypeIgnore(int lineno, string tag) - - type_param = TypeVar(identifier name, expr? bound) - | ParamSpec(identifier name) - | TypeVarTuple(identifier name) - attributes (int lineno, int col_offset, int end_lineno, int end_col_offset) } diff --git a/ast/asdl.py b/ast/asdl.py index 5c111b3e..e3e6c34d 100644 --- a/ast/asdl.py +++ b/ast/asdl.py @@ -176,7 +176,7 @@ def visitConstructor(self, cons, name): def visitField(self, field, name): key = str(field.type) - l = self.types.setdefault(key, []) # noqa + l = self.types.setdefault(key, []) l.append(name) def visitProduct(self, prod, name): @@ -193,7 +193,7 @@ def check(mod): v.visit(mod) for t in v.types: - if t not in mod.types and t not in builtin_types: + if t not in mod.types and not t in builtin_types: v.errors += 1 uses = ", ".join(v.types[t]) print('Undefined type {}, used in {}'.format(t, uses)) diff --git a/ast/asdl_rs.py b/ast/asdl_rs.py index 517a4b47..c9b819da 100755 --- a/ast/asdl_rs.py +++ b/ast/asdl_rs.py @@ -1,141 +1,34 @@ -# spell-checker:words dfn dfns - -# ! /usr/bin/env python +#! /usr/bin/env python """Generate Rust code from an ASDL description.""" import sys import json import textwrap -import re from argparse import ArgumentParser from pathlib import Path -from typing import Optional, Dict, Any import asdl TABSIZE = 4 -AUTO_GEN_MESSAGE = "// File automatically generated by {}.\n\n" +AUTOGEN_MESSAGE = "// File automatically generated by {}.\n" -BUILTIN_TYPE_NAMES = { - "identifier": "Identifier", +builtin_type_mapping = { + "identifier": "Ident", "string": "String", - "int": "Int", + "int": "usize", "constant": "Constant", } -assert BUILTIN_TYPE_NAMES.keys() == asdl.builtin_types - -BUILTIN_INT_NAMES = { - "simple": "bool", - "is_async": "bool", - "conversion": "ConversionFlag", -} - -RENAME_MAP = { - "cmpop": "cmp_op", - "unaryop": "unary_op", - "boolop": "bool_op", - "excepthandler": "except_handler", - "withitem": "with_item", -} - -RUST_KEYWORDS = { - "if", - "while", - "for", - "return", - "match", - "try", - "await", - "yield", - "in", - "mod", - "type", -} +assert builtin_type_mapping.keys() == asdl.builtin_types -attributes = [ - asdl.Field("int", "lineno"), - asdl.Field("int", "col_offset"), - asdl.Field("int", "end_lineno"), - asdl.Field("int", "end_col_offset"), -] - -ORIGINAL_NODE_WARNING = "NOTE: This type is different from original Python AST." - -arg_with_default = asdl.Type( - "arg_with_default", - asdl.Product( - [ - asdl.Field("arg", "def"), - asdl.Field( - "expr", "default", opt=True - ), # order is important for cost-free borrow! - ], - ), -) -arg_with_default.doc = f""" -An alternative type of AST `arg`. This is used for each function argument that might have a default value. -Used by `Arguments` original type. - -{ORIGINAL_NODE_WARNING} -""".strip() - -alt_arguments = asdl.Type( - "alt:arguments", - asdl.Product( - [ - asdl.Field("arg_with_default", "posonlyargs", seq=True), - asdl.Field("arg_with_default", "args", seq=True), - asdl.Field("arg", "vararg", opt=True), - asdl.Field("arg_with_default", "kwonlyargs", seq=True), - asdl.Field("arg", "kwarg", opt=True), - ] - ), -) -alt_arguments.doc = f""" -An alternative type of AST `arguments`. This is parser-friendly and human-friendly definition of function arguments. -This form also has advantage to implement pre-order traverse. -`defaults` and `kw_defaults` fields are removed and the default values are placed under each `arg_with_default` typed argument. -`vararg` and `kwarg` are still typed as `arg` because they never can have a default value. - -The matching Python style AST type is [PythonArguments]. While [PythonArguments] has ordered `kwonlyargs` fields by -default existence, [Arguments] has location-ordered kwonlyargs fields. - -{ORIGINAL_NODE_WARNING} -""".strip() - -# Must be used only for rust types, not python types -CUSTOM_TYPES = [ - alt_arguments, - arg_with_default, -] - -CUSTOM_REPLACEMENTS = { - "arguments": alt_arguments, -} -CUSTOM_ATTACHMENTS = [ - arg_with_default, -] - -def maybe_custom(type): - return CUSTOM_REPLACEMENTS.get(type.name, type) - - -def rust_field_name(name): - name = rust_type_name(name) - return re.sub(r"(?" - - @property - def name(self): - return self.type.name - - @property - def is_type(self): - return isinstance(self.type, asdl.Type) - - @property - def is_product(self): - return self.is_type and isinstance(self.type.value, asdl.Product) - - @property - def is_sum(self): - return self.is_type and isinstance(self.type.value, asdl.Sum) - - @property - def has_expr(self): - return self.is_product and any( - f.type != "identifier" for f in self.type.value.fields - ) - - @property - def is_custom(self): - return self.type.name in [t.name for t in CUSTOM_TYPES] - - @property - def is_custom_replaced(self): - return self.type.name in CUSTOM_REPLACEMENTS - - @property - def custom(self): - if self.type.name in CUSTOM_REPLACEMENTS: - return CUSTOM_REPLACEMENTS[self.type.name] - return self.type - - def no_cfg(self, typeinfo): - if self.is_product: - return self.has_attributes - elif self.enum_name: - return typeinfo[self.enum_name].has_attributes - else: - return self.has_attributes - - @property - def rust_name(self): - return rust_type_name(self.name) - - @property - def full_field_name(self): - name = self.name - if name.startswith("alt:"): - name = name[4:] - if self.enum_name is None: - return name - else: - return f"{self.enum_name}_{rust_field_name(name)}" - - @property - def full_type_name(self): - name = self.name - if name.startswith("alt:"): - name = name[4:] - rust_name = rust_type_name(name) - if self.enum_name is not None: - rust_name = rust_type_name(self.enum_name) + rust_name - if self.is_custom_replaced: - rust_name = "Python" + rust_name - return rust_name - - def determine_user_data(self, type_info, stack): - if self.name in stack: - return None - stack.add(self.name) - for child, child_seq in self.children: - if child in asdl.builtin_types: - continue - child_info = type_info[child] - child_has_user_data = child_info.determine_user_data(type_info, stack) - if self.has_user_data is None and child_has_user_data is True: - self.has_user_data = True - - stack.remove(self.name) - return self.has_user_data - - -class TypeInfoMixin: - type_info: Dict[str, TypeInfo] - - def customized_type_info(self, type_name): - info = self.type_info[type_name] - return self.type_info[info.custom.name] - - def has_user_data(self, typ): - return self.type_info[typ].has_user_data - - def apply_generics(self, typ, *generics): - needs_generics = not self.type_info[typ].is_simple - if needs_generics: - return [f"<{g}>" for g in generics] - else: - return ["" for g in generics] - - -class EmitVisitor(asdl.VisitorBase, TypeInfoMixin): +class EmitVisitor(asdl.VisitorBase): """Visit that emits lines""" - def __init__(self, file, type_info): + def __init__(self, file): self.file = file - self.type_info = type_info self.identifiers = set() super(EmitVisitor, self).__init__() @@ -314,1333 +79,340 @@ def emit_identifier(self, name): def emit(self, line, depth): if line: - line = (" " * TABSIZE * depth) + textwrap.dedent(line) + line = (" " * TABSIZE * depth) + line self.file.write(line + "\n") -class FindUserDataTypesVisitor(asdl.VisitorBase): - def __init__(self, type_info): - self.type_info = type_info +class TypeInfo: + def __init__(self, name): + self.name = name + self.has_userdata = None + self.children = set() + self.boxed = False + self.product = False + + def __repr__(self): + return f"" + + def determine_userdata(self, typeinfo, stack): + if self.name in stack: + return None + stack.add(self.name) + for child, child_seq in self.children: + if child in asdl.builtin_types: + continue + childinfo = typeinfo[child] + child_has_userdata = childinfo.determine_userdata(typeinfo, stack) + if self.has_userdata is None and child_has_userdata is True: + self.has_userdata = True + + stack.remove(self.name) + return self.has_userdata + + +class FindUserdataTypesVisitor(asdl.VisitorBase): + def __init__(self, typeinfo): + self.typeinfo = typeinfo super().__init__() def visitModule(self, mod): - for dfn in mod.dfns + CUSTOM_TYPES: + for dfn in mod.dfns: self.visit(dfn) stack = set() - for info in self.type_info.values(): - info.determine_user_data(self.type_info, stack) + for info in self.typeinfo.values(): + info.determine_userdata(self.typeinfo, stack) def visitType(self, type): - key = type.name - info = self.type_info[key] = TypeInfo(type) - self.visit(type.value, info) - - def visitSum(self, sum, info): - type = info.type - info.is_simple = is_simple(sum) - for cons in sum.types: - self.visit(cons, type, info.is_simple) - - if info.is_simple: - info.has_user_data = False - return - - for t in sum.types: - self.add_children(t.name, t.fields) - - if len(sum.types) > 1: - info.boxed = True - if sum.attributes: - # attributes means located, which has the `range: R` field - info.has_user_data = True - info.has_attributes = True + self.typeinfo[type.name] = TypeInfo(type.name) + self.visit(type.value, type.name) + def visitSum(self, sum, name): + info = self.typeinfo[name] + if is_simple(sum): + info.has_userdata = False + else: + if len(sum.types) > 1: + info.boxed = True + if sum.attributes: + # attributes means Located, which has the `custom: U` field + info.has_userdata = True for variant in sum.types: - self.add_children(type.name, variant.fields) + self.add_children(name, variant.fields) - def visitConstructor(self, cons, type, simple): - info = self.type_info[cons.name] = TypeInfo(cons) - info.enum_name = type.name - info.is_simple = simple - - def visitProduct(self, product, info): - type = info.type + def visitProduct(self, product, name): + info = self.typeinfo[name] if product.attributes: - # attributes means located, which has the `range: R` field - info.has_user_data = True - info.has_attributes = True + # attributes means Located, which has the `custom: U` field + info.has_userdata = True if len(product.fields) > 2: info.boxed = True - self.add_children(type.name, product.fields) + info.product = True + self.add_children(name, product.fields) def add_children(self, name, fields): - self.type_info[name].children.update( - (field.type, field.seq) for field in fields - ) + self.typeinfo[name].children.update((field.type, field.seq) for field in fields) def rust_field(field_name): - if field_name in RUST_KEYWORDS: - field_name += "_" - return field_name - - -class StructVisitor(EmitVisitor): - """Visitor to generate type-defs for AST.""" - - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - - def emit_attrs(self, depth): - self.emit("#[derive(Clone, Debug, PartialEq)]", depth) - - def emit_range(self, has_attributes, depth): - if has_attributes: - self.emit("pub range: R,", depth + 1) - else: - self.emit("pub range: OptionalRange,", depth + 1) - - def visitModule(self, mod): - self.emit_attrs(0) - self.emit( - """ - #[derive(is_macro::Is)] - pub enum Ast { - """, - 0, - ) - for dfn in mod.dfns: - info = self.customized_type_info(dfn.name) - dfn = info.custom - rust_name = info.full_type_name - generics = "" if self.type_info[dfn.name].is_simple else "" - if dfn.name == "mod": - # This is exceptional rule to other enums. - # Unlike other enums, this is justified because `Mod` is only used as - # the top node of parsing result and never a child node of other nodes. - # Because it will be very rarely used in very particular applications, - # "ast_" prefix to everywhere seems less useful. - self.emit('#[is(name = "module")]', 1) - self.emit(f"{rust_name}({rust_name}{generics}),", 1) - self.emit( - """ - } - impl Node for Ast { - const NAME: &'static str = "AST"; - const FIELD_NAMES: &'static [&'static str] = &[]; - } - """, - 0, - ) - for dfn in mod.dfns: - info = self.customized_type_info(dfn.name) - rust_name = info.full_type_name - generics = "" if self.type_info[dfn.name].is_simple else "" - self.emit( - f""" - impl From<{rust_name}{generics}> for Ast {{ - fn from(node: {rust_name}{generics}) -> Self {{ - Ast::{rust_name}(node) - }} - }} - """, - 0, - ) - - for dfn in mod.dfns + CUSTOM_TYPES: - self.visit(dfn) - - def visitType(self, type, depth=0): - if hasattr(type, "doc"): - doc = "/// " + type.doc.replace("\n", "\n/// ") + "\n" - else: - doc = f"/// See also [{type.name}](https://docs.python.org/3/library/ast.html#ast.{type.name})" - self.emit(doc, depth) - self.visit(type.value, type, depth) - - def visitSum(self, sum, type, depth): - if is_simple(sum): - self.simple_sum(sum, type, depth) - else: - self.sum_with_constructors(sum, type, depth) - - (generics_applied,) = self.apply_generics(type.name, "R") - self.emit( - f""" - impl{generics_applied} Node for {rust_type_name(type.name)}{generics_applied} {{ - const NAME: &'static str = "{type.name}"; - const FIELD_NAMES: &'static [&'static str] = &[]; - }} - """, - depth, - ) - - def simple_sum(self, sum, type, depth): - rust_name = rust_type_name(type.name) - self.emit_attrs(depth) - self.emit("#[derive(is_macro::Is, Copy, Hash, Eq)]", depth) - self.emit(f"pub enum {rust_name} {{", depth) - for cons in sum.types: - self.emit(f"{cons.name},", depth + 1) - self.emit("}", depth) - self.emit(f"impl {rust_name} {{", depth) - needs_escape = any(rust_field_name(t.name) in RUST_KEYWORDS for t in sum.types) - if needs_escape: - prefix = rust_field_name(type.name) + "_" - else: - prefix = "" - for cons in sum.types: - self.emit( - f""" - #[inline] - pub const fn {prefix}{rust_field_name(cons.name)}(&self) -> Option<{rust_name}{cons.name}> {{ - match self {{ - {rust_name}::{cons.name} => Some({rust_name}{cons.name}), - _ => None, - }} - }} - """, - depth, - ) - self.emit("}", depth) - self.emit("", depth) - - for cons in sum.types: - self.emit( - f""" - pub struct {rust_name}{cons.name}; - impl From<{rust_name}{cons.name}> for {rust_name} {{ - fn from(_: {rust_name}{cons.name}) -> Self {{ - {rust_name}::{cons.name} - }} - }} - impl From<{rust_name}{cons.name}> for Ast {{ - fn from(_: {rust_name}{cons.name}) -> Self {{ - {rust_name}::{cons.name}.into() - }} - }} - impl Node for {rust_name}{cons.name} {{ - const NAME: &'static str = "{cons.name}"; - const FIELD_NAMES: &'static [&'static str] = &[]; - }} - impl std::cmp::PartialEq<{rust_name}> for {rust_name}{cons.name} {{ - #[inline] - fn eq(&self, other: &{rust_name}) -> bool {{ - matches!(other, {rust_name}::{cons.name}) - }} - }} - """, - 0, - ) - - def sum_with_constructors(self, sum, type, depth): - type_info = self.type_info[type.name] - rust_name = rust_type_name(type.name) - - self.emit_attrs(depth) - self.emit("#[derive(is_macro::Is)]", depth) - self.emit(f"pub enum {rust_name} {{", depth) - needs_escape = any(rust_field_name(t.name) in RUST_KEYWORDS for t in sum.types) - for t in sum.types: - if needs_escape: - self.emit( - f'#[is(name = "{rust_field_name(t.name)}_{rust_name.lower()}")]', - depth + 1, - ) - self.emit(f"{t.name}({rust_name}{t.name}),", depth + 1) - self.emit("}", depth) - self.emit("", depth) - - for t in sum.types: - self.sum_subtype_struct(type_info, t, rust_name, depth) - + if field_name == "type": + return "type_" + else: + return field_name - def sum_subtype_struct(self, sum_type_info, t, rust_name, depth): - self.emit(f"""/// See also [{t.name}](https://docs.python.org/3/library/ast.html#ast.{t.name})""", depth) - self.emit_attrs(depth) - payload_name = f"{rust_name}{t.name}" - self.emit(f"pub struct {payload_name} {{", depth) - self.emit_range(sum_type_info.has_attributes, depth) - for f in t.fields: - self.visit(f, sum_type_info, "pub ", depth + 1, t.name) - - assert sum_type_info.has_attributes == self.type_info[t.name].no_cfg( - self.type_info - ) - self.emit("}", depth) - field_names = [f'"{f.name}"' for f in t.fields] - self.emit( - f""" - impl Node for {payload_name} {{ - const NAME: &'static str = "{t.name}"; - const FIELD_NAMES: &'static [&'static str] = &[{', '.join(field_names)}]; - }} - impl From<{payload_name}> for {rust_name} {{ - fn from(payload: {payload_name}) -> Self {{ - {rust_name}::{t.name}(payload) - }} - }} - impl From<{payload_name}> for Ast {{ - fn from(payload: {payload_name}) -> Self {{ - {rust_name}::from(payload).into() - }} - }} - """, - depth, - ) +class TypeInfoEmitVisitor(EmitVisitor): + def __init__(self, file, typeinfo): + self.typeinfo = typeinfo + super().__init__(file) - self.emit("", depth) + def has_userdata(self, typ): + return self.typeinfo[typ].has_userdata - def visitConstructor(self, cons, parent, depth): - if cons.fields: - self.emit(f"{cons.name} {{", depth) - for f in cons.fields: - self.visit(f, parent, "", depth + 1, cons.name) - self.emit("},", depth) + def get_generics(self, typ, *generics): + if self.has_userdata(typ): + return [f"<{g}>" for g in generics] else: - self.emit(f"{cons.name},", depth) - - def visitField(self, field, parent, vis, depth, constructor=None): - try: - field_type = self.customized_type_info(field.type) - typ = field_type.full_type_name - except KeyError: - field_type = None - typ = rust_type_name(field.type) - if field_type and not field_type.is_simple: - typ = f"{typ}" - # don't box if we're doing Vec, but do box if we're doing Vec>> - if ( - field_type - and field_type.boxed - and (not (parent.is_product or field.seq) or field.opt) - ): - typ = f"Box<{typ}>" - if field.opt or ( - # When a dictionary literal contains dictionary unpacking (e.g., `{**d}`), - # the expression to be unpacked goes in `values` with a `None` at the corresponding - # position in `keys`. To handle this, the type of `keys` needs to be `Option>`. - constructor == "Dict" - and field.name == "keys" - ): - typ = f"Option<{typ}>" - if field.seq: - typ = f"Vec<{typ}>" - if typ == "Int": - typ = BUILTIN_INT_NAMES.get(field.name, typ) - name = rust_field(field.name) - self.emit(f"{vis}{name}: {typ},", depth) - - def visitProduct(self, product, type, depth): - type_info = self.type_info[type.name] - product_name = type_info.full_type_name - self.emit_attrs(depth) - self.emit(f"pub struct {product_name} {{", depth) - self.emit_range(product.attributes, depth + 1) - for f in product.fields: - self.visit(f, type_info, "pub ", depth + 1) - assert bool(product.attributes) == type_info.no_cfg(self.type_info) - self.emit("}", depth) - - field_names = [f'"{f.name}"' for f in product.fields] - self.emit( - f""" - impl Node for {product_name} {{ - const NAME: &'static str = "{type.name}"; - const FIELD_NAMES: &'static [&'static str] = &[ - {', '.join(field_names)} - ]; - }} - """, - depth, - ) - - -class FoldTraitDefVisitor(EmitVisitor): - def visitModule(self, mod, depth): - self.emit("pub trait Fold {", depth) - self.emit("type TargetU;", depth + 1) - self.emit("type Error;", depth + 1) - self.emit("type UserContext;", depth + 1) - self.emit( - """ - fn will_map_user(&mut self, user: &U) -> Self::UserContext; - #[cfg(feature = "all-nodes-with-ranges")] - fn will_map_user_cfg(&mut self, user: &U) -> Self::UserContext { - self.will_map_user(user) - } - #[cfg(not(feature = "all-nodes-with-ranges"))] - fn will_map_user_cfg(&mut self, _user: &crate::EmptyRange) -> crate::EmptyRange { - crate::EmptyRange::default() - } - fn map_user(&mut self, user: U, context: Self::UserContext) -> Result; - #[cfg(feature = "all-nodes-with-ranges")] - fn map_user_cfg(&mut self, user: U, context: Self::UserContext) -> Result { - self.map_user(user, context) - } - #[cfg(not(feature = "all-nodes-with-ranges"))] - fn map_user_cfg( - &mut self, - _user: crate::EmptyRange, - _context: crate::EmptyRange, - ) -> Result, Self::Error> { - Ok(crate::EmptyRange::default()) - } - """, - depth + 1, - ) - self.emit( - """ - fn fold>(&mut self, node: X) -> Result { - node.fold(self) - }""", - depth + 1, - ) - for dfn in mod.dfns + [arg_with_default]: - dfn = maybe_custom(dfn) - self.visit(dfn, depth + 2) - self.emit("}", depth) - - def visitType(self, type, depth): - info = self.type_info[type.name] - apply_u, apply_target_u = self.apply_generics(info.name, "U", "Self::TargetU") - enum_name = info.full_type_name - self.emit( - f"fn fold_{info.full_field_name}(&mut self, node: {enum_name}{apply_u}) -> Result<{enum_name}{apply_target_u}, Self::Error> {{", - depth, - ) - self.emit(f"fold_{info.full_field_name}(self, node)", depth + 1) - self.emit("}", depth) - - if isinstance(type.value, asdl.Sum) and not is_simple(type.value): - for cons in type.value.types: - self.visit(cons, type, depth) - - def visitConstructor(self, cons, type, depth): - info = self.type_info[type.name] - apply_u, apply_target_u = self.apply_generics(type.name, "U", "Self::TargetU") - enum_name = rust_type_name(type.name) - func_name = f"fold_{info.full_field_name}_{rust_field_name(cons.name)}" - self.emit( - f"fn {func_name}(&mut self, node: {enum_name}{cons.name}{apply_u}) -> Result<{enum_name}{cons.name}{apply_target_u}, Self::Error> {{", - depth, - ) - self.emit(f"{func_name}(self, node)", depth + 1) - self.emit("}", depth) - - -class FoldImplVisitor(EmitVisitor): - def visitModule(self, mod, depth): - for dfn in mod.dfns + [arg_with_default]: - dfn = maybe_custom(dfn) - self.visit(dfn, depth) - - def visitType(self, type, depth=0): - self.visit(type.value, type, depth) - - def visitSum(self, sum, type, depth): - name = type.name - apply_t, apply_u, apply_target_u = self.apply_generics( - name, "T", "U", "F::TargetU" - ) - enum_name = rust_type_name(name) - simple = is_simple(sum) - - self.emit(f"impl Foldable for {enum_name}{apply_t} {{", depth) - self.emit(f"type Mapped = {enum_name}{apply_u};", depth + 1) - self.emit( - "fn fold + ?Sized>(self, folder: &mut F) -> Result {", - depth + 1, - ) - self.emit(f"folder.fold_{name}(self)", depth + 2) - self.emit("}", depth + 1) - self.emit("}", depth) - - self.emit( - f"pub fn fold_{name} + ?Sized>(#[allow(unused)] folder: &mut F, node: {enum_name}{apply_u}) -> Result<{enum_name}{apply_target_u}, F::Error> {{", - depth, - ) - - if simple: - self.emit("Ok(node) }", depth + 1) - return - - self.emit("let folded = match node {", depth + 1) - for cons in sum.types: - self.emit( - f"{enum_name}::{cons.name}(cons) => {enum_name}::{cons.name}(Foldable::fold(cons, folder)?),", - depth + 1, - ) - - self.emit("};", depth + 1) - self.emit("Ok(folded)", depth + 1) - self.emit("}", depth) - - for cons in sum.types: - self.visit(cons, type, depth) - - def visitConstructor(self, cons, type, depth): - apply_t, apply_u, apply_target_u = self.apply_generics( - type.name, "T", "U", "F::TargetU" - ) - info = self.type_info[type.name] - enum_name = info.full_type_name - - cons_type_name = f"{enum_name}{cons.name}" - - self.emit(f"impl Foldable for {cons_type_name}{apply_t} {{", depth) - self.emit(f"type Mapped = {cons_type_name}{apply_u};", depth + 1) - self.emit( - "fn fold + ?Sized>(self, folder: &mut F) -> Result {", - depth + 1, - ) - self.emit( - f"folder.fold_{info.full_field_name}_{rust_field_name(cons.name)}(self)", - depth + 2, - ) - self.emit("}", depth + 1) - self.emit("}", depth) - - self.emit( - f"pub fn fold_{info.full_field_name}_{rust_field_name(cons.name)} + ?Sized>(#[allow(unused)] folder: &mut F, node: {cons_type_name}{apply_u}) -> Result<{enum_name}{cons.name}{apply_target_u}, F::Error> {{", - depth, - ) - - fields_pattern = self.make_pattern(cons.fields) - - map_user_suffix = "" if info.has_attributes else "_cfg" - self.emit( - f""" - let {cons_type_name} {{ {fields_pattern} }} = node; - let context = folder.will_map_user{map_user_suffix}(&range); - """, - depth + 3, - ) - self.fold_fields(cons.fields, depth + 3) - self.emit( - f"let range = folder.map_user{map_user_suffix}(range, context)?;", - depth + 3, - ) - self.composite_fields(f"{cons_type_name}", cons.fields, depth + 3) - self.emit("}", depth + 2) - - def visitProduct(self, product, type, depth): - info = self.type_info[type.name] - name = type.name - apply_t, apply_u, apply_target_u = self.apply_generics( - name, "T", "U", "F::TargetU" - ) - struct_name = info.full_type_name - has_attributes = bool(product.attributes) - - self.emit(f"impl Foldable for {struct_name}{apply_t} {{", depth) - self.emit(f"type Mapped = {struct_name}{apply_u};", depth + 1) - self.emit( - "fn fold + ?Sized>(self, folder: &mut F) -> Result {", - depth + 1, - ) - self.emit(f"folder.fold_{info.full_field_name}(self)", depth + 2) - self.emit("}", depth + 1) - self.emit("}", depth) - - self.emit( - f"pub fn fold_{info.full_field_name} + ?Sized>(#[allow(unused)] folder: &mut F, node: {struct_name}{apply_u}) -> Result<{struct_name}{apply_target_u}, F::Error> {{", - depth, - ) - - fields_pattern = self.make_pattern(product.fields) - self.emit(f"let {struct_name} {{ {fields_pattern} }} = node;", depth + 1) - - map_user_suffix = "" if has_attributes else "_cfg" - - self.emit( - f"let context = folder.will_map_user{map_user_suffix}(&range);", depth + 3 - ) - self.fold_fields(product.fields, depth + 1) - self.emit( - f"let range = folder.map_user{map_user_suffix}(range, context)?;", depth + 3 - ) - self.composite_fields(struct_name, product.fields, depth + 1) - - self.emit("}", depth) - - def make_pattern(self, fields): - body = ",".join(rust_field(f.name) for f in fields) - if body: - body += "," - body += "range" - - return body + return ["" for g in generics] - def fold_fields(self, fields, depth): - for field in fields: - name = rust_field(field.name) - self.emit(f"let {name} = Foldable::fold({name}, folder)?;", depth + 1) - def composite_fields(self, header, fields, depth): - self.emit(f"Ok({header} {{", depth) - for field in fields: - name = rust_field(field.name) - self.emit(f"{name},", depth + 1) - self.emit("range,", depth + 1) - self.emit("})", depth) +class StructVisitor(TypeInfoEmitVisitor): + """Visitor to generate typedefs for AST.""" - -class FoldModuleVisitor(EmitVisitor): def visitModule(self, mod): - depth = 0 - FoldTraitDefVisitor(self.file, self.type_info).visit(mod, depth) - FoldImplVisitor(self.file, self.type_info).visit(mod, depth) - - -class VisitorModuleVisitor(StructVisitor): - def full_name(self, name): - type_info = self.type_info[name] - if type_info.enum_name: - return f"{type_info.enum_name}_{name}" - else: - return name - - def node_type_name(self, name): - type_info = self.type_info[name] - if type_info.enum_name: - return f"{rust_type_name(type_info.enum_name)}{rust_type_name(name)}" - else: - return rust_type_name(name) - - def visitModule(self, mod, depth=0): - self.emit("#[allow(unused_variables)]", depth) - self.emit("pub trait Visitor {", depth) - for dfn in mod.dfns: - dfn = self.customized_type_info(dfn.name).type - self.visit(dfn, depth + 1) - self.emit("}", depth) - - def visitType(self, type, depth=0): - self.visit(type.value, type.name, depth) - - def visitSum(self, sum, name, depth): - if is_simple(sum): - self.simple_sum(sum, name, depth) - else: - self.sum_with_constructors(sum, name, depth) - - def emit_visitor(self, nodename, depth, has_node=True): - type_info = self.type_info[nodename] - node_type = type_info.full_type_name - (generic,) = self.apply_generics(nodename, "R") - self.emit( - f"fn visit_{type_info.full_field_name}(&mut self, node: {node_type}{generic}) {{", - depth, - ) - if has_node: - self.emit( - f"self.generic_visit_{type_info.full_field_name}(node)", depth + 1 - ) - - self.emit("}", depth) - - def emit_generic_visitor_signature(self, nodename, depth, has_node=True): - type_info = self.type_info[nodename] - if has_node: - node_type = type_info.full_type_name - else: - node_type = "()" - (generic,) = self.apply_generics(nodename, "R") - self.emit( - f"fn generic_visit_{type_info.full_field_name}(&mut self, node: {node_type}{generic}) {{", - depth, - ) - - def emit_empty_generic_visitor(self, nodename, depth): - self.emit_generic_visitor_signature(nodename, depth) - self.emit("}", depth) - - def simple_sum(self, sum, name, depth): - self.emit_visitor(name, depth) - self.emit_empty_generic_visitor(name, depth) - - def visit_match_for_type(self, nodename, rust_name, type_, depth): - self.emit(f"{rust_name}::{type_.name}", depth) - self.emit("(data)", depth) - self.emit( - f"=> self.visit_{nodename}_{rust_field_name(type_.name)}(data),", depth - ) - - def visit_sum_type(self, name, type_, depth): - self.emit_visitor(type_.name, depth, has_node=type_.fields) - if not type_.fields: - return - - self.emit_generic_visitor_signature(type_.name, depth, has_node=True) - for field in type_.fields: - if field.type in CUSTOM_REPLACEMENTS: - type_name = CUSTOM_REPLACEMENTS[field.type].name - else: - type_name = field.type - field_name = rust_field(field.name) - field_type = self.type_info.get(type_name) - if not (field_type and field_type.has_user_data): - continue - - if field.opt: - self.emit(f"if let Some(value) = node.{field_name} {{", depth + 1) - elif field.seq: - iterable = f"node.{field_name}" - if type_.name == "Dict" and field.name == "keys": - iterable = f"{iterable}.into_iter().flatten()" - self.emit(f"for value in {iterable} {{", depth + 1) - else: - self.emit("{", depth + 1) - self.emit(f"let value = node.{field_name};", depth + 2) - - variable = "value" - if field_type.boxed and (not field.seq or field.opt): - variable = "*" + variable - type_info = self.type_info[field_type.name] - self.emit(f"self.visit_{type_info.full_field_name}({variable});", depth + 2) - - self.emit("}", depth + 1) - - self.emit("}", depth) - - def sum_with_constructors(self, sum, name, depth): - if not sum.attributes: - return - - enum_name = rust_type_name(name) - self.emit_visitor(name, depth) - self.emit_generic_visitor_signature(name, depth) - depth += 1 - self.emit("match node {", depth) - for t in sum.types: - self.visit_match_for_type(name, enum_name, t, depth + 1) - self.emit("}", depth) - depth -= 1 - self.emit("}", depth) - - # Now for the visitors for the types - for t in sum.types: - self.visit_sum_type(name, t, depth) - - def visitProduct(self, product, name, depth): - self.emit_visitor(name, depth) - self.emit_empty_generic_visitor(name, depth) - - -class RangedDefVisitor(EmitVisitor): - def visitModule(self, mod): - for dfn in mod.dfns + CUSTOM_TYPES: - self.visit(dfn) - - def visitType(self, type, depth=0): - self.visit(type.value, type.name, depth) - - def visitSum(self, sum, name, depth): - info = self.type_info[name] - - self.emit_type_alias(info) - - if info.is_simple: - for ty in sum.types: - variant_info = self.type_info[ty.name] - self.emit_type_alias(variant_info) - return - - sum_match_arms = "" - - for ty in sum.types: - variant_info = self.type_info[ty.name] - sum_match_arms += ( - f" Self::{variant_info.rust_name}(node) => node.range()," - ) - self.emit_type_alias(variant_info) - self.emit_ranged_impl(variant_info) - - if not info.no_cfg(self.type_info): - self.emit('#[cfg(feature = "all-nodes-with-ranges")]', 0) - - self.emit( - f""" - impl Ranged for crate::{info.full_type_name} {{ - fn range(&self) -> TextRange {{ - match self {{ - {sum_match_arms} - }} - }} - }} - """.lstrip(), - 0, - ) - - def visitProduct(self, product, name, depth): - info = self.type_info[name] - - self.emit_type_alias(info) - self.emit_ranged_impl(info) - - def emit_type_alias(self, info): - return # disable - generics = "" if info.is_simple else "::" - - self.emit( - f"pub type {info.full_type_name} = crate::generic::{info.full_type_name}{generics};", - 0, - ) - self.emit("", 0) - - def emit_ranged_impl(self, info): - if not info.no_cfg(self.type_info): - self.emit('#[cfg(feature = "all-nodes-with-ranges")]', 0) - - self.file.write( - f""" - impl Ranged for crate::generic::{info.full_type_name}:: {{ - fn range(&self) -> TextRange {{ - self.range - }} - }} - """.strip() - ) - - -class LocatedDefVisitor(EmitVisitor): - def visitModule(self, mod): - for dfn in mod.dfns + CUSTOM_TYPES: self.visit(dfn) def visitType(self, type, depth=0): self.visit(type.value, type.name, depth) - def visitSum(self, sum, name, depth): - info = self.type_info[name] - - self.emit_type_alias(info) - - if info.is_simple: - for ty in sum.types: - variant_info = self.type_info[ty.name] - self.emit_type_alias(variant_info) - return - - sum_match_arms = "" - - for ty in sum.types: - variant_info = self.type_info[ty.name] - sum_match_arms += ( - f" Self::{variant_info.rust_name}(node) => node.range()," - ) - self.emit_type_alias(variant_info) - self.emit_located_impl(variant_info) - - if not info.no_cfg(self.type_info): - cfg = '#[cfg(feature = "all-nodes-with-ranges")]' - else: - cfg = '' - - self.emit( - f""" - {cfg} - impl Located for {info.full_type_name} {{ - fn range(&self) -> SourceRange {{ - match self {{ - {sum_match_arms} - }} - }} - }} - {cfg} - impl LocatedMut for {info.full_type_name} {{ - fn range_mut(&mut self) -> &mut SourceRange {{ - match self {{ - {sum_match_arms.replace('range()', 'range_mut()')} - }} - }} - }} - """.lstrip(), - 0, - ) - - def visitProduct(self, product, name, depth): - info = self.type_info[name] - - self.emit_type_alias(info) - self.emit_located_impl(info) - - def emit_type_alias(self, info): - generics = "" if info.is_simple else "::" - - self.emit( - f"pub type {info.full_type_name} = crate::generic::{info.full_type_name}{generics};", - 0, - ) - self.emit("", 0) - - def emit_located_impl(self, info): - if not info.no_cfg(self.type_info): - cfg = '#[cfg(feature = "all-nodes-with-ranges")]' - else: - cfg = '' - - self.emit( - f""" - {cfg} - impl Located for {info.full_type_name} {{ - fn range(&self) -> SourceRange {{ - self.range - }} - }} - {cfg} - impl LocatedMut for {info.full_type_name} {{ - fn range_mut(&mut self) -> &mut SourceRange {{ - &mut self.range - }} - }} - """, - 0, - ) - - -class ToPyo3AstVisitor(EmitVisitor): - """Visitor to generate type-defs for AST.""" - - def __init__(self, namespace, *args, **kw): - super().__init__(*args, **kw) - self.namespace = namespace - - @property - def generics(self): - if self.namespace == "ranged": - return "" - elif self.namespace == "located": - return "" - else: - assert False, self.namespace - - def visitModule(self, mod): - for dfn in mod.dfns: - self.visit(dfn) - - def visitType(self, type): - self.visit(type.value, type) - - def visitProduct(self, product, type): - info = self.type_info[type.name] - rust_name = info.full_type_name - self.emit_to_pyo3_with_fields(product, type, rust_name) - - def visitSum(self, sum, type): - info = self.type_info[type.name] - rust_name = info.full_type_name - simple = is_simple(sum) - if is_simple(sum): - return - - self.emit( - f""" - impl ToPyAst for ast::{rust_name}{self.generics} {{ - #[inline] - fn to_py_ast<'py>(&self, {"_" if simple else ""}py: Python<'py>) -> PyResult<&'py PyAny> {{ - let instance = match &self {{ - """, - 0, - ) - for cons in sum.types: - self.emit( - f"ast::{rust_name}::{cons.name}(cons) => cons.to_py_ast(py)?,", - 1, - ) - self.emit( - """ - }; - Ok(instance) - } - } - """, - 0, - ) - - for cons in sum.types: - self.visit(cons, type) - - def visitConstructor(self, cons, type): - parent = rust_type_name(type.name) - self.emit_to_pyo3_with_fields(cons, type, f"{parent}{cons.name}") - - def emit_to_pyo3_with_fields(self, cons, type, name): - type_info = self.type_info[type.name] - - self.emit( - f""" - impl ToPyAst for ast::{name}{self.generics} {{ - #[inline] - fn to_py_ast<'py>(&self, py: Python<'py>) -> PyResult<&'py PyAny> {{ - let cache = Self::py_type_cache().get().unwrap(); - """, - 0, - ) - if cons.fields: - field_names = ", ".join(rust_field(f.name) for f in cons.fields) - if not type_info.is_simple: - field_names += ", range: _range" - self.emit( - f"let Self {{ {field_names} }} = self;", - 1, - ) - self.emit( - """ - let instance = Py::::as_ref(&cache.0, py).call1(( - """, - 1, - ) - for field in cons.fields: - if field.type == "constant": - self.emit( - f"constant_to_object({rust_field(field.name)}, py),", - 3, - ) - continue - if field.type == "int": - if field.name == "level": - assert field.opt - self.emit( - f"{rust_field(field.name)}.map_or_else(|| py.None(), |level| level.to_u32().to_object(py)),", - 3, - ) - continue - if field.name == "lineno": - self.emit( - f"{rust_field(field.name)}.to_u32().to_object(py),", - 3, - ) - continue - self.emit( - f"{rust_field(field.name)}.to_py_ast(py)?,", - 3, - ) - self.emit( - "))?;", - 0, - ) - else: - self.emit( - "let Self { range: _range } = self;", - 1, - ) - self.emit( - """let instance = Py::::as_ref(&cache.0, py).call0()?;""", - 1, - ) - if type.value.attributes and self.namespace == "located": - self.emit( - """ - let cache = ast_cache(); - instance.setattr(cache.lineno.as_ref(py), _range.start.row.get())?; - instance.setattr(cache.col_offset.as_ref(py), _range.start.column.get())?; - if let Some(end) = _range.end { - instance.setattr(cache.end_lineno.as_ref(py), end.row.get())?; - instance.setattr(cache.end_col_offset.as_ref(py), end.column.get())?; - } - """, - 0, - ) - self.emit( - """ - Ok(instance) - } - } - """, - 0, - ) - - -class Pyo3StructVisitor(EmitVisitor): - """Visitor to generate type-defs for AST.""" - - def __init__(self, namespace, *args, **kw): - self.namespace = namespace - self.borrow = True - super().__init__(*args, **kw) - - @property - def generics(self): - if self.namespace == "ranged": - return "" - elif self.namespace == "located": - return "" + def visitSum(self, sum, name, depth): + if is_simple(sum): + self.simple_sum(sum, name, depth) else: - assert False, self.namespace - - @property - def module_name(self): - name = f"rustpython_ast.{self.namespace}" - return name - - @property - def ref_def(self): - return "&'static " if self.borrow else "" + self.sum_with_constructors(sum, name, depth) - @property - def ref(self): - return "&" if self.borrow else "" + def emit_attrs(self, depth): + self.emit("#[derive(Clone, Debug, PartialEq)]", depth) - def emit_class(self, info, simple, base="super::Ast"): - inner_name = info.full_type_name - rust_name = self.type_info[info.custom.name].full_type_name - if simple: - generics = "" - else: - generics = self.generics - if info.is_sum: - subclass = ", subclass" - body = "" - into = f"{rust_name}" - else: - subclass = "" - body = f"(pub {self.ref_def} ast::{inner_name}{generics})" - into = f"{rust_name}(node)" + def simple_sum(self, sum, name, depth): + rustname = get_rust_type(name) + self.emit_attrs(depth) + self.emit(f"pub enum {rustname} {{", depth) + for variant in sum.types: + self.emit(f"{variant.name},", depth + 1) + self.emit("}", depth) + self.emit("", depth) - self.emit( - f""" - #[pyclass(module="{self.module_name}", name="_{info.name}", extends={base}, frozen{subclass})] - #[derive(Clone, Debug)] - pub struct {rust_name} {body}; - - impl From<{self.ref_def} ast::{inner_name}{generics}> for {rust_name} {{ - fn from({"" if body else "_"}node: {self.ref_def} ast::{inner_name}{generics}) -> Self {{ - {into} - }} - }} - """, - 0, - ) - - if subclass: - self.emit( - f""" - #[pymethods] - impl {rust_name} {{ - #[new] - fn new() -> PyClassInitializer {{ - PyClassInitializer::from(Ast) - .add_subclass(Self) - }} - - }} - impl ToPyObject for {rust_name} {{ - fn to_object(&self, py: Python) -> PyObject {{ - let initializer = Self::new(); - Py::new(py, initializer).unwrap().into_py(py) - }} - }} - """, - 0, - ) - else: - if base != "super::Ast": - add_subclass = f".add_subclass({base})" - else: - add_subclass = "" + def sum_with_constructors(self, sum, name, depth): + typeinfo = self.typeinfo[name] + generics, generics_applied = self.get_generics(name, "U = ()", "U") + enumname = rustname = get_rust_type(name) + # all the attributes right now are for location, so if it has attrs we + # can just wrap it in Located<> + if sum.attributes: + enumname = rustname + "Kind" + self.emit_attrs(depth) + self.emit(f"pub enum {enumname}{generics} {{", depth) + for t in sum.types: + self.visit(t, typeinfo, depth + 1) + self.emit("}", depth) + if sum.attributes: self.emit( - f""" - impl ToPyObject for {rust_name} {{ - fn to_object(&self, py: Python) -> PyObject {{ - let initializer = PyClassInitializer::from(Ast) - {add_subclass} - .add_subclass(self.clone()); - Py::new(py, initializer).unwrap().into_py(py) - }} - }} - """, - 0, + f"pub type {rustname} = Located<{enumname}{generics_applied}, U>;", + depth, ) + self.emit("", depth) - if not subclass: - self.emit_wrapper(info) + def visitConstructor(self, cons, parent, depth): + if cons.fields: + self.emit(f"{cons.name} {{", depth) + for f in cons.fields: + self.visit(f, parent, "", depth + 1, cons.name) + self.emit("},", depth) + else: + self.emit(f"{cons.name},", depth) - def emit_getter(self, owner, type_name): - self.emit( - f""" - #[pymethods] - impl {type_name} {{ - """, - 0, - ) + def visitField(self, field, parent, vis, depth, constructor=None): + typ = get_rust_type(field.type) + fieldtype = self.typeinfo.get(field.type) + if fieldtype and fieldtype.has_userdata: + typ = f"{typ}" + # don't box if we're doing Vec, but do box if we're doing Vec>> + if fieldtype and fieldtype.boxed and (not (parent.product or field.seq) or field.opt): + typ = f"Box<{typ}>" + if field.opt or ( + # When a dictionary literal contains dictionary unpacking (e.g., `{**d}`), + # the expression to be unpacked goes in `values` with a `None` at the corresponding + # position in `keys`. To handle this, the type of `keys` needs to be `Option>`. + constructor == "Dict" and field.name == "keys" + ): + typ = f"Option<{typ}>" + if field.seq: + typ = f"Vec<{typ}>" + name = rust_field(field.name) + self.emit(f"{vis}{name}: {typ},", depth) - for field in owner.fields: + def visitProduct(self, product, name, depth): + typeinfo = self.typeinfo[name] + generics, generics_applied = self.get_generics(name, "U = ()", "U") + dataname = rustname = get_rust_type(name) + if product.attributes: + dataname = rustname + "Data" + self.emit_attrs(depth) + has_expr = any(f.type != "identifier" for f in product.fields) + if has_expr: + datadef = f"{dataname}{generics}" + else: + datadef = dataname + self.emit(f"pub struct {datadef} {{", depth) + for f in product.fields: + self.visit(f, typeinfo, "pub ", depth + 1) + self.emit("}", depth) + if product.attributes: + # attributes should just be location info + if not has_expr: + generics_applied = "" self.emit( - f""" - #[getter] - #[inline] - fn get_{field.name}(&self, py: Python) -> PyResult {{ - self.0.{rust_field(field.name)}.to_py_wrapper(py) - }} - """, - 3, + f"pub type {rustname} = Located<{dataname}{generics_applied}, U>;", + depth, ) + self.emit("", depth) + +class FoldTraitDefVisitor(TypeInfoEmitVisitor): + def visitModule(self, mod, depth): + self.emit("pub trait Fold {", depth) + self.emit("type TargetU;", depth + 1) + self.emit("type Error;", depth + 1) self.emit( - """ - } - """, - 0, + "fn map_user(&mut self, user: U) -> Result;", + depth + 2, ) + for dfn in mod.dfns: + self.visit(dfn, depth + 2) + self.emit("}", depth) - def emit_getattr(self, owner, type_name): + def visitType(self, type, depth): + name = type.name + apply_u, apply_target_u = self.get_generics(name, "U", "Self::TargetU") + enumname = get_rust_type(name) self.emit( - f""" - #[pymethods] - impl {type_name} {{ - fn __getattr__(&self, py: Python, key: &str) -> PyResult {{ - let object: Py = match key {{ - """, - 0, + f"fn fold_{name}(&mut self, node: {enumname}{apply_u}) -> Result<{enumname}{apply_target_u}, Self::Error> {{", + depth, ) + self.emit(f"fold_{name}(self, node)", depth + 1) + self.emit("}", depth) - for field in owner.fields: - self.emit( - f'"{field.name}" => self.0.{rust_field(field.name)}.to_py_wrapper(py)?,', - 3, - ) +class FoldImplVisitor(TypeInfoEmitVisitor): + def visitModule(self, mod, depth): self.emit( - """ - _ => todo!(), - }; - Ok(object) - } - } - """, - 0, + "fn fold_located + ?Sized, T, MT>(folder: &mut F, node: Located, f: impl FnOnce(&mut F, T) -> Result) -> Result, F::Error> {", + depth, ) - - def emit_wrapper(self, info): - inner_name = info.full_type_name - rust_name = self.type_info[info.custom.name].full_type_name self.emit( - f""" - impl ToPyWrapper for ast::{inner_name}{self.generics} {{ - #[inline] - fn to_py_wrapper(&'static self, py: Python) -> PyResult> {{ - Ok({rust_name}(self).to_object(py)) - }} - }} - """, - 0, + "Ok(Located { custom: folder.map_user(node.custom)?, location: node.location, end_location: node.end_location, node: f(folder, node.node)? })", + depth + 1, ) - - def visitModule(self, mod): + self.emit("}", depth) for dfn in mod.dfns: - self.visit(dfn) + self.visit(dfn, depth) def visitType(self, type, depth=0): - self.visit(type.value, type, depth) - - def visitSum(self, sum, type, depth=0): - info = self.type_info[type.name] - rust_name = rust_type_name(type.name) - - simple = is_simple(sum) - self.emit_class(info, simple) - - if not simple: - self.emit( - f""" - impl ToPyWrapper for ast::{rust_name}{self.generics} {{ - #[inline] - fn to_py_wrapper(&'static self, py: Python) -> PyResult> {{ - match &self {{ - """, - 0, - ) + self.visit(type.value, type.name, depth) - for cons in sum.types: - self.emit(f"Self::{cons.name}(cons) => cons.to_py_wrapper(py),", 3) + def visitSum(self, sum, name, depth): + apply_t, apply_u, apply_target_u = self.get_generics( + name, "T", "U", "F::TargetU" + ) + enumname = get_rust_type(name) + is_located = bool(sum.attributes) - self.emit( - """ - } - } - } - """, - 0, - ) + self.emit(f"impl Foldable for {enumname}{apply_t} {{", depth) + self.emit(f"type Mapped = {enumname}{apply_u};", depth + 1) + self.emit( + "fn fold + ?Sized>(self, folder: &mut F) -> Result {", + depth + 1, + ) + self.emit(f"folder.fold_{name}(self)", depth + 2) + self.emit("}", depth + 1) + self.emit("}", depth) + self.emit( + f"pub fn fold_{name} + ?Sized>(#[allow(unused)] folder: &mut F, node: {enumname}{apply_u}) -> Result<{enumname}{apply_target_u}, F::Error> {{", + depth, + ) + if is_located: + self.emit("fold_located(folder, node, |folder, node| {", depth) + enumname += "Kind" + self.emit("match node {", depth + 1) for cons in sum.types: - self.visit(cons, rust_name, simple, depth + 1) - - def visitProduct(self, product, type, depth=0): - info = self.type_info[type.name] - rust_name = rust_type_name(type.name) - self.emit_class(info, False) - if self.borrow: - self.emit_getter(product, rust_name) - - def visitConstructor(self, cons, parent, simple, depth): - if simple: + fields_pattern = self.make_pattern(cons.fields) self.emit( - f""" - #[pyclass(module="{self.module_name}", name="_{cons.name}", extends={parent})] - pub struct {parent}{cons.name}; - - impl ToPyObject for {parent}{cons.name} {{ - fn to_object(&self, py: Python) -> PyObject {{ - let initializer = PyClassInitializer::from(Ast) - .add_subclass({parent}) - .add_subclass(Self); - Py::new(py, initializer).unwrap().into_py(py) - }} - }} - """, - depth, - ) - else: - info = self.type_info[cons.name] - self.emit_class( - info, - simple=False, - base=parent, + f"{enumname}::{cons.name} {{ {fields_pattern} }} => {{", depth + 2 ) - if self.borrow: - self.emit_getter(cons, f"{parent}{cons.name}") - - -class Pyo3PymoduleVisitor(EmitVisitor): - def __init__(self, namespace, *args, **kw): - self.namespace = namespace - super().__init__(*args, **kw) + self.gen_construction(f"{enumname}::{cons.name}", cons.fields, depth + 3) + self.emit("}", depth + 2) + self.emit("}", depth + 1) + if is_located: + self.emit("})", depth) + self.emit("}", depth) - def visitModule(self, mod): - for dfn in mod.dfns: - self.visit(dfn) + def visitProduct(self, product, name, depth): + apply_t, apply_u, apply_target_u = self.get_generics( + name, "T", "U", "F::TargetU" + ) + structname = get_rust_type(name) + is_located = bool(product.attributes) - def visitType(self, type, depth=0): - self.visit(type.value, type.name, depth) + self.emit(f"impl Foldable for {structname}{apply_t} {{", depth) + self.emit(f"type Mapped = {structname}{apply_u};", depth + 1) + self.emit( + "fn fold + ?Sized>(self, folder: &mut F) -> Result {", + depth + 1, + ) + self.emit(f"folder.fold_{name}(self)", depth + 2) + self.emit("}", depth + 1) + self.emit("}", depth) - def visitProduct(self, product, name, depth=0): - info = self.type_info[name] - self.emit_fields(info, False) + self.emit( + f"pub fn fold_{name} + ?Sized>(#[allow(unused)] folder: &mut F, node: {structname}{apply_u}) -> Result<{structname}{apply_target_u}, F::Error> {{", + depth, + ) + if is_located: + self.emit("fold_located(folder, node, |folder, node| {", depth) + structname += "Data" + fields_pattern = self.make_pattern(product.fields) + self.emit(f"let {structname} {{ {fields_pattern} }} = node;", depth + 1) + self.gen_construction(structname, product.fields, depth + 1) + if is_located: + self.emit("})", depth) + self.emit("}", depth) - def visitSum(self, sum, name, depth): - info = self.type_info[name] - simple = is_simple(sum) - self.emit_fields(info, True) + def make_pattern(self, fields): + return ",".join(rust_field(f.name) for f in fields) - for cons in sum.types: - self.visit(cons, name, simple, depth) + def gen_construction(self, cons_path, fields, depth): + self.emit(f"Ok({cons_path} {{", depth) + for field in fields: + name = rust_field(field.name) + self.emit(f"{name}: Foldable::fold({name}, folder)?,", depth + 1) + self.emit("})", depth) - def visitConstructor(self, cons, parent, simple, depth): - info = self.type_info[cons.name] - self.emit_fields(info, simple) - def emit_fields(self, info, simple): - inner_name = info.full_type_name - rust_name = self.type_info[info.custom.name].full_type_name - self.emit(f"super::init_type::<{rust_name}, ast::{inner_name}>(py, m)?;", 1) +class FoldModuleVisitor(TypeInfoEmitVisitor): + def visitModule(self, mod): + depth = 0 + self.emit('#[cfg(feature = "fold")]', depth) + self.emit("pub mod fold {", depth) + self.emit("use super::*;", depth + 1) + self.emit("use crate::fold_helpers::Foldable;", depth + 1) + FoldTraitDefVisitor(self.file, self.typeinfo).visit(mod, depth + 1) + FoldImplVisitor(self.file, self.typeinfo).visit(mod, depth + 1) + self.emit("}", depth) -class StdlibClassDefVisitor(EmitVisitor): +class ClassDefVisitor(EmitVisitor): def visitModule(self, mod): for dfn in mod.dfns: self.visit(dfn) @@ -1649,41 +421,33 @@ def visitType(self, type, depth=0): self.visit(type.value, type.name, depth) def visitSum(self, sum, name, depth): - # info = self.type_info[self.type_info[name].custom.name] - info = self.type_info[name] - struct_name = "Node" + info.full_type_name + structname = "NodeKind" + get_rust_type(name) self.emit( - f'#[pyclass(module = "_ast", name = {json.dumps(name)}, base = "NodeAst")]', + f'#[pyclass(module = "_ast", name = {json.dumps(name)}, base = "AstNode")]', depth, ) - self.emit(f"struct {struct_name};", depth) + self.emit(f"struct {structname};", depth) self.emit("#[pyclass(flags(HAS_DICT, BASETYPE))]", depth) - self.emit(f"impl {struct_name} {{}}", depth) + self.emit(f"impl {structname} {{}}", depth) for cons in sum.types: - self.visit(cons, sum.attributes, struct_name, depth) + self.visit(cons, sum.attributes, structname, depth) def visitConstructor(self, cons, attrs, base, depth): - self.gen_class_def(cons.name, cons.fields, attrs, depth, base) + self.gen_classdef(cons.name, cons.fields, attrs, depth, base) def visitProduct(self, product, name, depth): - self.gen_class_def(name, product.fields, product.attributes, depth) - - def gen_class_def(self, name, fields, attrs, depth, base=None): + self.gen_classdef(name, product.fields, product.attributes, depth) - info = self.type_info[self.type_info[name].custom.name] - if base is None: - base = "NodeAst" - struct_name = "Node" + info.full_type_name - else: - struct_name = "Node" + info.full_type_name + def gen_classdef(self, name, fields, attrs, depth, base="AstNode"): + structname = "Node" + get_rust_type(name) self.emit( f'#[pyclass(module = "_ast", name = {json.dumps(name)}, base = {json.dumps(base)})]', depth, ) - self.emit(f"struct {struct_name};", depth) + self.emit(f"struct {structname};", depth) self.emit("#[pyclass(flags(HAS_DICT, BASETYPE))]", depth) - self.emit(f"impl {struct_name} {{", depth) - self.emit("#[extend_class]", depth + 1) + self.emit(f"impl {structname} {{", depth) + self.emit(f"#[extend_class]", depth + 1) self.emit( "fn extend_class_with_fields(ctx: &Context, class: &'static Py) {", depth + 1, @@ -1706,7 +470,7 @@ def gen_class_def(self, name, fields, attrs, depth, base=None): self.emit("}", depth) -class StdlibExtendModuleVisitor(EmitVisitor): +class ExtendModuleVisitor(EmitVisitor): def visitModule(self, mod): depth = 0 self.emit( @@ -1723,25 +487,25 @@ def visitType(self, type, depth): self.visit(type.value, type.name, depth) def visitSum(self, sum, name, depth): - rust_name = rust_type_name(name) - self.emit(f"{json.dumps(name)} => Node{rust_name}::make_class(&vm.ctx),", depth) + rust_name = get_rust_type(name) + self.emit( + f"{json.dumps(name)} => NodeKind{rust_name}::make_class(&vm.ctx),", depth + ) for cons in sum.types: - self.visit(cons, depth, rust_name) + self.visit(cons, depth) - def visitConstructor(self, cons, depth, rust_name): - self.gen_extension(cons.name, depth, rust_name) + def visitConstructor(self, cons, depth): + self.gen_extension(cons.name, depth) def visitProduct(self, product, name, depth): self.gen_extension(name, depth) - def gen_extension(self, name, depth, base=""): - rust_name = rust_type_name(name) - self.emit( - f"{json.dumps(name)} => Node{base}{rust_name}::make_class(&vm.ctx),", depth - ) + def gen_extension(self, name, depth): + rust_name = get_rust_type(name) + self.emit(f"{json.dumps(name)} => Node{rust_name}::make_class(&vm.ctx),", depth) -class StdlibTraitImplVisitor(EmitVisitor): +class TraitImplVisitor(EmitVisitor): def visitModule(self, mod): for dfn in mod.dfns: self.visit(dfn) @@ -1750,186 +514,115 @@ def visitType(self, type, depth=0): self.visit(type.value, type.name, depth) def visitSum(self, sum, name, depth): - info = self.type_info[name] - rust_name = info.full_type_name - - self.emit("// sum", depth) - self.emit(f"impl Node for ast::located::{rust_name} {{", depth) - self.emit( - "fn ast_to_object(self, vm: &VirtualMachine) -> PyObjectRef {", depth + 1 - ) - simple = is_simple(sum) - if simple: - self.emit("let node_type = match self {", depth + 2) - for cons in sum.types: - self.emit( - f"ast::located::{rust_name}::{cons.name} => Node{rust_name}{cons.name}::static_type(),", - depth, - ) - self.emit("};", depth + 3) - self.emit( - "NodeAst.into_ref_with_type(vm, node_type.to_owned()).unwrap().into()", - depth + 2, - ) - else: - self.emit("match self {", depth + 2) - for cons in sum.types: - self.emit( - f"ast::located::{rust_name}::{cons.name}(cons) => cons.ast_to_object(vm),", - depth + 3, - ) - self.emit("}", depth + 2) + enumname = get_rust_type(name) + if sum.attributes: + enumname += "Kind" - self.emit("}", depth + 1) - self.emit( - "fn ast_from_object(_vm: &VirtualMachine, _object: PyObjectRef) -> PyResult {", - depth + 1, - ) - self.gen_sum_from_object(sum, name, rust_name, depth + 2) - self.emit("}", depth + 1) + self.emit(f"impl NamedNode for ast::{enumname} {{", depth) + self.emit(f"const NAME: &'static str = {json.dumps(name)};", depth + 1) self.emit("}", depth) - - if not is_simple(sum): - for cons in sum.types: - self.visit(cons, sum, rust_name, depth) - - def visitConstructor(self, cons, sum, sum_rust_name, depth): - rust_name = rust_type_name(cons.name) - self.emit("// constructor", depth) - self.emit(f"impl Node for ast::located::{sum_rust_name}{rust_name} {{", depth) - - fields_pattern = self.make_pattern(cons.fields) - + self.emit(f"impl Node for ast::{enumname} {{", depth) self.emit( "fn ast_to_object(self, _vm: &VirtualMachine) -> PyObjectRef {", depth + 1 ) - self.emit( - f"let ast::located::{sum_rust_name}{rust_name} {{ {fields_pattern} }} = self;", - depth, - ) - self.make_node(cons.name, sum, cons.fields, depth + 2, sum_rust_name) - + self.emit("match self {", depth + 2) + for variant in sum.types: + self.constructor_to_object(variant, enumname, depth + 3) + self.emit("}", depth + 2) self.emit("}", depth + 1) - self.emit( "fn ast_from_object(_vm: &VirtualMachine, _object: PyObjectRef) -> PyResult {", depth + 1, ) - - self.gen_product_from_object( - cons, cons.name, f"{sum_rust_name}{rust_name}", sum.attributes, depth + 2 - ) + self.gen_sum_fromobj(sum, name, enumname, depth + 2) self.emit("}", depth + 1) + self.emit("}", depth) - self.emit("}", depth + 1) + def constructor_to_object(self, cons, enumname, depth): + fields_pattern = self.make_pattern(cons.fields) + self.emit(f"ast::{enumname}::{cons.name} {{ {fields_pattern} }} => {{", depth) + self.make_node(cons.name, cons.fields, depth + 1) + self.emit("}", depth) def visitProduct(self, product, name, depth): - info = self.type_info[name] - struct_name = info.full_type_name + structname = get_rust_type(name) + if product.attributes: + structname += "Data" - self.emit("// product", depth) - self.emit(f"impl Node for ast::located::{struct_name} {{", depth) + self.emit(f"impl NamedNode for ast::{structname} {{", depth) + self.emit(f"const NAME: &'static str = {json.dumps(name)};", depth + 1) + self.emit("}", depth) + self.emit(f"impl Node for ast::{structname} {{", depth) self.emit( "fn ast_to_object(self, _vm: &VirtualMachine) -> PyObjectRef {", depth + 1 ) fields_pattern = self.make_pattern(product.fields) - self.emit( - f"let ast::located::{struct_name} {{ {fields_pattern} }} = self;", - depth + 2, - ) - self.make_node(name, product, product.fields, depth + 2) + self.emit(f"let ast::{structname} {{ {fields_pattern} }} = self;", depth + 2) + self.make_node(name, product.fields, depth + 2) self.emit("}", depth + 1) self.emit( "fn ast_from_object(_vm: &VirtualMachine, _object: PyObjectRef) -> PyResult {", depth + 1, ) - self.gen_product_from_object( - product, name, struct_name, product.attributes, depth + 2 - ) + self.gen_product_fromobj(product, name, structname, depth + 2) self.emit("}", depth + 1) self.emit("}", depth) - def make_node(self, variant, owner, fields, depth, base=""): - rust_variant = rust_type_name(variant) + def make_node(self, variant, fields, depth): + rust_variant = get_rust_type(variant) self.emit( - f"let node = NodeAst.into_ref_with_type(_vm, Node{base}{rust_variant}::static_type().to_owned()).unwrap();", + f"let _node = AstNode.into_ref_with_type(_vm, Node{rust_variant}::static_type().to_owned()).unwrap();", depth, ) - if fields or owner.attributes: - self.emit("let dict = node.as_object().dict().unwrap();", depth) + if fields: + self.emit("let _dict = _node.as_object().dict().unwrap();", depth) for f in fields: self.emit( - f"dict.set_item({json.dumps(f.name)}, {rust_field(f.name)}.ast_to_object(_vm), _vm).unwrap();", + f"_dict.set_item({json.dumps(f.name)}, {rust_field(f.name)}.ast_to_object(_vm), _vm).unwrap();", depth, ) - if owner.attributes: - self.emit("node_add_location(&dict, _range, _vm);", depth) - self.emit("node.into()", depth) + self.emit("_node.into()", depth) def make_pattern(self, fields): - return "".join(f"{rust_field(f.name)}," for f in fields) + "range: _range" + return ",".join(rust_field(f.name) for f in fields) - def gen_sum_from_object(self, sum, sum_name, rust_name, depth): - # if sum.attributes: - # self.extract_location(sum_name, depth) + def gen_sum_fromobj(self, sum, sumname, enumname, depth): + if sum.attributes: + self.extract_location(sumname, depth) self.emit("let _cls = _object.class();", depth) self.emit("Ok(", depth) for cons in sum.types: - self.emit( - f"if _cls.is(Node{rust_name}{cons.name}::static_type()) {{", depth - ) - self.emit(f"ast::located::{rust_name}::{cons.name}", depth + 1) - if not is_simple(sum): - self.emit( - f"(ast::located::{rust_name}{cons.name}::ast_from_object(_vm, _object)?)", - depth + 1, - ) + self.emit(f"if _cls.is(Node{cons.name}::static_type()) {{", depth) + self.gen_construction(f"{enumname}::{cons.name}", cons, sumname, depth + 1) self.emit("} else", depth) self.emit("{", depth) - msg = f'format!("expected some sort of {sum_name}, but got {{}}",_object.repr(_vm)?)' + msg = f'format!("expected some sort of {sumname}, but got {{}}",_object.repr(_vm)?)' self.emit(f"return Err(_vm.new_type_error({msg}));", depth + 1) self.emit("})", depth) - def gen_product_from_object( - self, product, product_name, struct_name, has_attributes, depth - ): + def gen_product_fromobj(self, product, prodname, structname, depth): + if product.attributes: + self.extract_location(prodname, depth) + self.emit("Ok(", depth) - self.gen_construction( - struct_name, product, product_name, has_attributes, depth + 1 - ) + self.gen_construction(structname, product, prodname, depth + 1) self.emit(")", depth) - def gen_construction_fields(self, cons, name, depth): + def gen_construction(self, cons_path, cons, name, depth): + self.emit(f"ast::{cons_path} {{", depth) for field in cons.fields: self.emit( f"{rust_field(field.name)}: {self.decode_field(field, name)},", depth + 1, ) - - def gen_construction(self, cons_path, cons, name, attributes, depth): - self.emit(f"ast::located::{cons_path} {{", depth) - self.gen_construction_fields(cons, name, depth + 1) - if attributes: - self.emit(f'range: range_from_object(_vm, _object, "{name}")?,', depth + 1) - else: - self.emit("range: Default::default(),", depth + 1) self.emit("}", depth) def extract_location(self, typename, depth): row = self.decode_field(asdl.Field("int", "lineno"), typename) column = self.decode_field(asdl.Field("int", "col_offset"), typename) - self.emit( - f""" - let _location = {{ - let row = {row}; - let column = {column}; - try_location(row, column) - }}; - """, - depth, - ) + self.emit(f"let _location = ast::Location::new({row}, {column});", depth) def decode_field(self, field, typename): name = json.dumps(field.name) @@ -1949,222 +642,82 @@ def visit(self, object): v.emit("", 0) -def write_ast_def(mod, type_info, f): - f.write("use crate::text_size::TextRange;") - StructVisitor(f, type_info).visit(mod) - - -def write_fold_def(mod, type_info, f): - FoldModuleVisitor(f, type_info).visit(mod) - - -def write_visitor_def(mod, type_info, f): - VisitorModuleVisitor(f, type_info).visit(mod) - - -def write_ranged_def(mod, type_info, f): - RangedDefVisitor(f, type_info).visit(mod) - - -def write_located_def(mod, type_info, f): - LocatedDefVisitor(f, type_info).visit(mod) - - -def write_pyo3_node(type_info, f): - def write(info: TypeInfo, rust_name: str): - if info.is_simple: - generics = "" - else: - generics = "" - - f.write( - f""" - impl{generics} PyNode for ast::{rust_name}{generics} {{ - #[inline] - fn py_type_cache() -> &'static OnceCell<(Py, Py)> {{ - static PY_TYPE: OnceCell<(Py, Py)> = OnceCell::new(); - &PY_TYPE - }} - }} - """, - ) - - for type_name, info in type_info.items(): - rust_name = info.full_type_name - if info.is_custom: - if type_name != info.type.name: - rust_name = "Python" + rust_name - else: - continue - write(info, rust_name) - - -def write_to_pyo3(mod, type_info, f): - write_pyo3_node(type_info, f) - write_to_pyo3_simple(type_info, f) - - for namespace in ("ranged", "located"): - ToPyo3AstVisitor(namespace, f, type_info).visit(mod) - +def write_ast_def(mod, typeinfo, f): f.write( - """ - fn init_types(py: Python) -> PyResult<()> { - let ast_module = PyModule::import(py, "_ast")?; - """ - ) + textwrap.dedent( + """ + #![allow(clippy::derive_partial_eq_without_eq)] + + pub use crate::constant::*; + pub use crate::Location; - for info in type_info.values(): - if info.is_custom: - continue - rust_name = info.full_type_name - f.write(f"cache_py_type::(ast_module)?;\n") - f.write("Ok(())\n}") - - -def write_to_pyo3_simple(type_info, f): - for type_info in type_info.values(): - if not type_info.is_sum: - continue - if not type_info.is_simple: - continue - - rust_name = type_info.full_type_name - f.write( - f""" - impl ToPyAst for ast::{rust_name} {{ - #[inline] - fn to_py_ast<'py>(&self, py: Python<'py>) -> PyResult<&'py PyAny> {{ - let cell = match &self {{ - """, + type Ident = String; + \n + """ ) - for cons in type_info.type.value.types: - f.write( - f"""ast::{rust_name}::{cons.name} => ast::{rust_name}{cons.name}::py_type_cache(),""", - ) - f.write( + ) + StructVisitor(f, typeinfo).emit_attrs(0) + f.write( + textwrap.dedent( """ - }; - Ok(Py::::as_ref(&cell.get().unwrap().1, py)) - } + pub struct Located { + pub location: Location, + pub end_location: Option, + pub custom: U, + pub node: T, + } + + impl Located { + pub fn new(location: Location, end_location: Location, node: T) -> Self { + Self { location, end_location: Some(end_location), custom: (), node } } - """, + + pub const fn start(&self) -> Location { + self.location + } + + /// Returns the node's [`end_location`](Located::end_location) or [`location`](Located::start) if + /// [`end_location`](Located::end_location) is `None`. + pub fn end(&self) -> Location { + self.end_location.unwrap_or(self.location) + } + } + + impl std::ops::Deref for Located { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.node + } + } + \n + """.lstrip() ) + ) + c = ChainOfVisitors(StructVisitor(f, typeinfo), FoldModuleVisitor(f, typeinfo)) + c.visit(mod) -def write_pyo3_wrapper(mod, type_info, namespace, f): - Pyo3StructVisitor(namespace, f, type_info).visit(mod) - - if namespace == "located": - for info in type_info.values(): - if not info.is_simple or not info.is_sum: - continue - - rust_name = info.full_type_name - inner_name = type_info[info.custom.name].full_type_name - f.write( - f""" - impl ToPyWrapper for ast::{inner_name} {{ - #[inline] - fn to_py_wrapper(&self, py: Python) -> PyResult> {{ - match &self {{ - """, - ) - for cons in info.type.value.types: - f.write( - f"Self::{cons.name} => Ok({rust_name}{cons.name}.to_object(py)),", - ) - f.write( - """ - } - } - } - """, - ) - - for cons in info.type.value.types: - f.write( - f""" - impl ToPyWrapper for ast::{rust_name}{cons.name} {{ - #[inline] - fn to_py_wrapper(&self, py: Python) -> PyResult> {{ - Ok({rust_name}{cons.name}.to_object(py)) - }} - }} - """ - ) - - f.write( - """ - pub fn add_to_module(py: Python, m: &PyModule) -> PyResult<()> { - super::init_module(py, m)?; - """ - ) - Pyo3PymoduleVisitor(namespace, f, type_info).visit(mod) - f.write("Ok(())\n}") - - -def write_parse_def(mod, type_info, f): - for info in type_info.values(): - if info.enum_name not in ["expr", "stmt"]: - continue - - type_name = rust_type_name(info.enum_name) - cons_name = rust_type_name(info.name) - - f.write(f""" - impl Parse for ast::{info.full_type_name} {{ - fn lex_starts_at( - source: &str, - offset: TextSize, - ) -> SoftKeywordTransformer> {{ - ast::{type_name}::lex_starts_at(source, offset) - }} - fn parse_tokens( - lxr: impl IntoIterator, - source_path: &str, - ) -> Result {{ - let node = ast::{type_name}::parse_tokens(lxr, source_path)?; - match node {{ - ast::{type_name}::{cons_name}(node) => Ok(node), - node => Err(ParseError {{ - error: ParseErrorType::InvalidToken, - offset: node.range().start(), - source_path: source_path.to_owned(), - }}), - }} - }} - }} - """) - - -def write_ast_mod(mod, type_info, f): +def write_ast_mod(mod, f): f.write( - """ + textwrap.dedent( + """ #![allow(clippy::all)] use super::*; use crate::common::ascii; - """ - ) - c = ChainOfVisitors( - StdlibClassDefVisitor(f, type_info), - StdlibTraitImplVisitor(f, type_info), - StdlibExtendModuleVisitor(f, type_info), + """ + ) ) + + c = ChainOfVisitors(ClassDefVisitor(f), TraitImplVisitor(f), ExtendModuleVisitor(f)) c.visit(mod) -def main( - input_filename, - ast_dir, - parser_dir, - ast_pyo3_dir, - module_filename, - dump_module=False, -): - auto_gen_msg = AUTO_GEN_MESSAGE.format("/".join(Path(__file__).parts[-2:])) +def main(input_filename, ast_mod_filename, ast_def_filename, dump_module=False): + auto_gen_msg = AUTOGEN_MESSAGE.format("/".join(Path(__file__).parts[-2:])) mod = asdl.parse(input_filename) if dump_module: print("Parsed Module:") @@ -2172,61 +725,25 @@ def main( if not asdl.check(mod): sys.exit(1) - type_info = {} - FindUserDataTypesVisitor(type_info).visit(mod) - - from functools import partial as p + typeinfo = {} + FindUserdataTypesVisitor(typeinfo).visit(mod) - for filename, write in [ - ("generic", p(write_ast_def, mod, type_info)), - ("fold", p(write_fold_def, mod, type_info)), - ("ranged", p(write_ranged_def, mod, type_info)), - ("located", p(write_located_def, mod, type_info)), - ("visitor", p(write_visitor_def, mod, type_info)), - ]: - with (ast_dir / f"{filename}.rs").open("w") as f: - f.write(auto_gen_msg) - write(f) + with ast_def_filename.open("w") as def_file, ast_mod_filename.open("w") as mod_file: + def_file.write(auto_gen_msg) + write_ast_def(mod, typeinfo, def_file) - for filename, write in [ - ("parse", p(write_parse_def, mod, type_info)), - ]: - with (parser_dir / f"{filename}.rs").open("w") as f: - f.write(auto_gen_msg) - write(f) + mod_file.write(auto_gen_msg) + write_ast_mod(mod, mod_file) - - for filename, write in [ - ("to_py_ast", p(write_to_pyo3, mod, type_info)), - ("wrapper_located", p(write_pyo3_wrapper, mod, type_info, "located")), - ("wrapper_ranged", p(write_pyo3_wrapper, mod, type_info, "ranged")), - ]: - with (ast_pyo3_dir / f"{filename}.rs").open("w") as f: - f.write(auto_gen_msg) - write(f) - - with module_filename.open("w") as module_file: - module_file.write(auto_gen_msg) - write_ast_mod(mod, type_info, module_file) - - print(f"{ast_dir}, {module_filename} regenerated.") + print(f"{ast_def_filename}, {ast_mod_filename} regenerated.") if __name__ == "__main__": parser = ArgumentParser() parser.add_argument("input_file", type=Path) - parser.add_argument("-A", "--ast-dir", type=Path, required=True) - parser.add_argument("-P", "--parser-dir", type=Path, required=True) - parser.add_argument("-O", "--ast-pyo3-dir", type=Path, required=True) - parser.add_argument("-M", "--module-file", type=Path, required=True) + parser.add_argument("-M", "--mod-file", type=Path, required=True) + parser.add_argument("-D", "--def-file", type=Path, required=True) parser.add_argument("-d", "--dump-module", action="store_true") args = parser.parse_args() - main( - args.input_file, - args.ast_dir, - args.parser_dir, - args.ast_pyo3_dir, - args.module_file, - args.dump_module, - ) + main(args.input_file, args.mod_file, args.def_file, args.dump_module) diff --git a/ast/src/ast_gen.rs b/ast/src/ast_gen.rs new file mode 100644 index 00000000..6771dd0e --- /dev/null +++ b/ast/src/ast_gen.rs @@ -0,0 +1,1320 @@ +// File automatically generated by ast/asdl_rs.py. + +#![allow(clippy::derive_partial_eq_without_eq)] + +pub use crate::constant::*; +pub use crate::Location; + +type Ident = String; + +#[derive(Clone, Debug, PartialEq)] +pub struct Located { + pub location: Location, + pub end_location: Option, + pub custom: U, + pub node: T, +} + +impl Located { + pub fn new(location: Location, end_location: Location, node: T) -> Self { + Self { + location, + end_location: Some(end_location), + custom: (), + node, + } + } + + pub const fn start(&self) -> Location { + self.location + } + + /// Returns the node's [`end_location`](Located::end_location) or [`location`](Located::start) if + /// [`end_location`](Located::end_location) is `None`. + pub fn end(&self) -> Location { + self.end_location.unwrap_or(self.location) + } +} + +impl std::ops::Deref for Located { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.node + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Mod { + Module { + body: Vec>, + type_ignores: Vec, + }, + Interactive { + body: Vec>, + }, + Expression { + body: Box>, + }, + FunctionType { + argtypes: Vec>, + returns: Box>, + }, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum StmtKind { + FunctionDef { + name: Ident, + args: Box>, + body: Vec>, + decorator_list: Vec>, + returns: Option>>, + type_comment: Option, + }, + AsyncFunctionDef { + name: Ident, + args: Box>, + body: Vec>, + decorator_list: Vec>, + returns: Option>>, + type_comment: Option, + }, + ClassDef { + name: Ident, + bases: Vec>, + keywords: Vec>, + body: Vec>, + decorator_list: Vec>, + }, + Return { + value: Option>>, + }, + Delete { + targets: Vec>, + }, + Assign { + targets: Vec>, + value: Box>, + type_comment: Option, + }, + AugAssign { + target: Box>, + op: Operator, + value: Box>, + }, + AnnAssign { + target: Box>, + annotation: Box>, + value: Option>>, + simple: usize, + }, + For { + target: Box>, + iter: Box>, + body: Vec>, + orelse: Vec>, + type_comment: Option, + }, + AsyncFor { + target: Box>, + iter: Box>, + body: Vec>, + orelse: Vec>, + type_comment: Option, + }, + While { + test: Box>, + body: Vec>, + orelse: Vec>, + }, + If { + test: Box>, + body: Vec>, + orelse: Vec>, + }, + With { + items: Vec>, + body: Vec>, + type_comment: Option, + }, + AsyncWith { + items: Vec>, + body: Vec>, + type_comment: Option, + }, + Match { + subject: Box>, + cases: Vec>, + }, + Raise { + exc: Option>>, + cause: Option>>, + }, + Try { + body: Vec>, + handlers: Vec>, + orelse: Vec>, + finalbody: Vec>, + }, + TryStar { + body: Vec>, + handlers: Vec>, + orelse: Vec>, + finalbody: Vec>, + }, + Assert { + test: Box>, + msg: Option>>, + }, + Import { + names: Vec>, + }, + ImportFrom { + module: Option, + names: Vec>, + level: Option, + }, + Global { + names: Vec, + }, + Nonlocal { + names: Vec, + }, + Expr { + value: Box>, + }, + Pass, + Break, + Continue, +} +pub type Stmt = Located, U>; + +#[derive(Clone, Debug, PartialEq)] +pub enum ExprKind { + BoolOp { + op: Boolop, + values: Vec>, + }, + NamedExpr { + target: Box>, + value: Box>, + }, + BinOp { + left: Box>, + op: Operator, + right: Box>, + }, + UnaryOp { + op: Unaryop, + operand: Box>, + }, + Lambda { + args: Box>, + body: Box>, + }, + IfExp { + test: Box>, + body: Box>, + orelse: Box>, + }, + Dict { + keys: Vec>>, + values: Vec>, + }, + Set { + elts: Vec>, + }, + ListComp { + elt: Box>, + generators: Vec>, + }, + SetComp { + elt: Box>, + generators: Vec>, + }, + DictComp { + key: Box>, + value: Box>, + generators: Vec>, + }, + GeneratorExp { + elt: Box>, + generators: Vec>, + }, + Await { + value: Box>, + }, + Yield { + value: Option>>, + }, + YieldFrom { + value: Box>, + }, + Compare { + left: Box>, + ops: Vec, + comparators: Vec>, + }, + Call { + func: Box>, + args: Vec>, + keywords: Vec>, + }, + FormattedValue { + value: Box>, + conversion: usize, + format_spec: Option>>, + }, + JoinedStr { + values: Vec>, + }, + Constant { + value: Constant, + kind: Option, + }, + Attribute { + value: Box>, + attr: Ident, + ctx: ExprContext, + }, + Subscript { + value: Box>, + slice: Box>, + ctx: ExprContext, + }, + Starred { + value: Box>, + ctx: ExprContext, + }, + Name { + id: Ident, + ctx: ExprContext, + }, + List { + elts: Vec>, + ctx: ExprContext, + }, + Tuple { + elts: Vec>, + ctx: ExprContext, + }, + Slice { + lower: Option>>, + upper: Option>>, + step: Option>>, + }, +} +pub type Expr = Located, U>; + +#[derive(Clone, Debug, PartialEq)] +pub enum ExprContext { + Load, + Store, + Del, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Boolop { + And, + Or, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Operator { + Add, + Sub, + Mult, + MatMult, + Div, + Mod, + Pow, + LShift, + RShift, + BitOr, + BitXor, + BitAnd, + FloorDiv, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Unaryop { + Invert, + Not, + UAdd, + USub, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Cmpop { + Eq, + NotEq, + Lt, + LtE, + Gt, + GtE, + Is, + IsNot, + In, + NotIn, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Comprehension { + pub target: Expr, + pub iter: Expr, + pub ifs: Vec>, + pub is_async: usize, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ExcepthandlerKind { + ExceptHandler { + type_: Option>>, + name: Option, + body: Vec>, + }, +} +pub type Excepthandler = Located, U>; + +#[derive(Clone, Debug, PartialEq)] +pub struct Arguments { + pub posonlyargs: Vec>, + pub args: Vec>, + pub vararg: Option>>, + pub kwonlyargs: Vec>, + pub kw_defaults: Vec>, + pub kwarg: Option>>, + pub defaults: Vec>, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ArgData { + pub arg: Ident, + pub annotation: Option>>, + pub type_comment: Option, +} +pub type Arg = Located, U>; + +#[derive(Clone, Debug, PartialEq)] +pub struct KeywordData { + pub arg: Option, + pub value: Expr, +} +pub type Keyword = Located, U>; + +#[derive(Clone, Debug, PartialEq)] +pub struct AliasData { + pub name: Ident, + pub asname: Option, +} +pub type Alias = Located; + +#[derive(Clone, Debug, PartialEq)] +pub struct Withitem { + pub context_expr: Expr, + pub optional_vars: Option>>, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct MatchCase { + pub pattern: Pattern, + pub guard: Option>>, + pub body: Vec>, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum PatternKind { + MatchValue { + value: Box>, + }, + MatchSingleton { + value: Constant, + }, + MatchSequence { + patterns: Vec>, + }, + MatchMapping { + keys: Vec>, + patterns: Vec>, + rest: Option, + }, + MatchClass { + cls: Box>, + patterns: Vec>, + kwd_attrs: Vec, + kwd_patterns: Vec>, + }, + MatchStar { + name: Option, + }, + MatchAs { + pattern: Option>>, + name: Option, + }, + MatchOr { + patterns: Vec>, + }, +} +pub type Pattern = Located, U>; + +#[derive(Clone, Debug, PartialEq)] +pub enum TypeIgnore { + TypeIgnore { lineno: usize, tag: String }, +} + +#[cfg(feature = "fold")] +pub mod fold { + use super::*; + use crate::fold_helpers::Foldable; + pub trait Fold { + type TargetU; + type Error; + fn map_user(&mut self, user: U) -> Result; + fn fold_mod(&mut self, node: Mod) -> Result, Self::Error> { + fold_mod(self, node) + } + fn fold_stmt(&mut self, node: Stmt) -> Result, Self::Error> { + fold_stmt(self, node) + } + fn fold_expr(&mut self, node: Expr) -> Result, Self::Error> { + fold_expr(self, node) + } + fn fold_expr_context(&mut self, node: ExprContext) -> Result { + fold_expr_context(self, node) + } + fn fold_boolop(&mut self, node: Boolop) -> Result { + fold_boolop(self, node) + } + fn fold_operator(&mut self, node: Operator) -> Result { + fold_operator(self, node) + } + fn fold_unaryop(&mut self, node: Unaryop) -> Result { + fold_unaryop(self, node) + } + fn fold_cmpop(&mut self, node: Cmpop) -> Result { + fold_cmpop(self, node) + } + fn fold_comprehension( + &mut self, + node: Comprehension, + ) -> Result, Self::Error> { + fold_comprehension(self, node) + } + fn fold_excepthandler( + &mut self, + node: Excepthandler, + ) -> Result, Self::Error> { + fold_excepthandler(self, node) + } + fn fold_arguments( + &mut self, + node: Arguments, + ) -> Result, Self::Error> { + fold_arguments(self, node) + } + fn fold_arg(&mut self, node: Arg) -> Result, Self::Error> { + fold_arg(self, node) + } + fn fold_keyword( + &mut self, + node: Keyword, + ) -> Result, Self::Error> { + fold_keyword(self, node) + } + fn fold_alias(&mut self, node: Alias) -> Result, Self::Error> { + fold_alias(self, node) + } + fn fold_withitem( + &mut self, + node: Withitem, + ) -> Result, Self::Error> { + fold_withitem(self, node) + } + fn fold_match_case( + &mut self, + node: MatchCase, + ) -> Result, Self::Error> { + fold_match_case(self, node) + } + fn fold_pattern( + &mut self, + node: Pattern, + ) -> Result, Self::Error> { + fold_pattern(self, node) + } + fn fold_type_ignore(&mut self, node: TypeIgnore) -> Result { + fold_type_ignore(self, node) + } + } + fn fold_located + ?Sized, T, MT>( + folder: &mut F, + node: Located, + f: impl FnOnce(&mut F, T) -> Result, + ) -> Result, F::Error> { + Ok(Located { + custom: folder.map_user(node.custom)?, + location: node.location, + end_location: node.end_location, + node: f(folder, node.node)?, + }) + } + impl Foldable for Mod { + type Mapped = Mod; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_mod(self) + } + } + pub fn fold_mod + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Mod, + ) -> Result, F::Error> { + match node { + Mod::Module { body, type_ignores } => Ok(Mod::Module { + body: Foldable::fold(body, folder)?, + type_ignores: Foldable::fold(type_ignores, folder)?, + }), + Mod::Interactive { body } => Ok(Mod::Interactive { + body: Foldable::fold(body, folder)?, + }), + Mod::Expression { body } => Ok(Mod::Expression { + body: Foldable::fold(body, folder)?, + }), + Mod::FunctionType { argtypes, returns } => Ok(Mod::FunctionType { + argtypes: Foldable::fold(argtypes, folder)?, + returns: Foldable::fold(returns, folder)?, + }), + } + } + impl Foldable for Stmt { + type Mapped = Stmt; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_stmt(self) + } + } + pub fn fold_stmt + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Stmt, + ) -> Result, F::Error> { + fold_located(folder, node, |folder, node| match node { + StmtKind::FunctionDef { + name, + args, + body, + decorator_list, + returns, + type_comment, + } => Ok(StmtKind::FunctionDef { + name: Foldable::fold(name, folder)?, + args: Foldable::fold(args, folder)?, + body: Foldable::fold(body, folder)?, + decorator_list: Foldable::fold(decorator_list, folder)?, + returns: Foldable::fold(returns, folder)?, + type_comment: Foldable::fold(type_comment, folder)?, + }), + StmtKind::AsyncFunctionDef { + name, + args, + body, + decorator_list, + returns, + type_comment, + } => Ok(StmtKind::AsyncFunctionDef { + name: Foldable::fold(name, folder)?, + args: Foldable::fold(args, folder)?, + body: Foldable::fold(body, folder)?, + decorator_list: Foldable::fold(decorator_list, folder)?, + returns: Foldable::fold(returns, folder)?, + type_comment: Foldable::fold(type_comment, folder)?, + }), + StmtKind::ClassDef { + name, + bases, + keywords, + body, + decorator_list, + } => Ok(StmtKind::ClassDef { + name: Foldable::fold(name, folder)?, + bases: Foldable::fold(bases, folder)?, + keywords: Foldable::fold(keywords, folder)?, + body: Foldable::fold(body, folder)?, + decorator_list: Foldable::fold(decorator_list, folder)?, + }), + StmtKind::Return { value } => Ok(StmtKind::Return { + value: Foldable::fold(value, folder)?, + }), + StmtKind::Delete { targets } => Ok(StmtKind::Delete { + targets: Foldable::fold(targets, folder)?, + }), + StmtKind::Assign { + targets, + value, + type_comment, + } => Ok(StmtKind::Assign { + targets: Foldable::fold(targets, folder)?, + value: Foldable::fold(value, folder)?, + type_comment: Foldable::fold(type_comment, folder)?, + }), + StmtKind::AugAssign { target, op, value } => Ok(StmtKind::AugAssign { + target: Foldable::fold(target, folder)?, + op: Foldable::fold(op, folder)?, + value: Foldable::fold(value, folder)?, + }), + StmtKind::AnnAssign { + target, + annotation, + value, + simple, + } => Ok(StmtKind::AnnAssign { + target: Foldable::fold(target, folder)?, + annotation: Foldable::fold(annotation, folder)?, + value: Foldable::fold(value, folder)?, + simple: Foldable::fold(simple, folder)?, + }), + StmtKind::For { + target, + iter, + body, + orelse, + type_comment, + } => Ok(StmtKind::For { + target: Foldable::fold(target, folder)?, + iter: Foldable::fold(iter, folder)?, + body: Foldable::fold(body, folder)?, + orelse: Foldable::fold(orelse, folder)?, + type_comment: Foldable::fold(type_comment, folder)?, + }), + StmtKind::AsyncFor { + target, + iter, + body, + orelse, + type_comment, + } => Ok(StmtKind::AsyncFor { + target: Foldable::fold(target, folder)?, + iter: Foldable::fold(iter, folder)?, + body: Foldable::fold(body, folder)?, + orelse: Foldable::fold(orelse, folder)?, + type_comment: Foldable::fold(type_comment, folder)?, + }), + StmtKind::While { test, body, orelse } => Ok(StmtKind::While { + test: Foldable::fold(test, folder)?, + body: Foldable::fold(body, folder)?, + orelse: Foldable::fold(orelse, folder)?, + }), + StmtKind::If { test, body, orelse } => Ok(StmtKind::If { + test: Foldable::fold(test, folder)?, + body: Foldable::fold(body, folder)?, + orelse: Foldable::fold(orelse, folder)?, + }), + StmtKind::With { + items, + body, + type_comment, + } => Ok(StmtKind::With { + items: Foldable::fold(items, folder)?, + body: Foldable::fold(body, folder)?, + type_comment: Foldable::fold(type_comment, folder)?, + }), + StmtKind::AsyncWith { + items, + body, + type_comment, + } => Ok(StmtKind::AsyncWith { + items: Foldable::fold(items, folder)?, + body: Foldable::fold(body, folder)?, + type_comment: Foldable::fold(type_comment, folder)?, + }), + StmtKind::Match { subject, cases } => Ok(StmtKind::Match { + subject: Foldable::fold(subject, folder)?, + cases: Foldable::fold(cases, folder)?, + }), + StmtKind::Raise { exc, cause } => Ok(StmtKind::Raise { + exc: Foldable::fold(exc, folder)?, + cause: Foldable::fold(cause, folder)?, + }), + StmtKind::Try { + body, + handlers, + orelse, + finalbody, + } => Ok(StmtKind::Try { + body: Foldable::fold(body, folder)?, + handlers: Foldable::fold(handlers, folder)?, + orelse: Foldable::fold(orelse, folder)?, + finalbody: Foldable::fold(finalbody, folder)?, + }), + StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, + } => Ok(StmtKind::TryStar { + body: Foldable::fold(body, folder)?, + handlers: Foldable::fold(handlers, folder)?, + orelse: Foldable::fold(orelse, folder)?, + finalbody: Foldable::fold(finalbody, folder)?, + }), + StmtKind::Assert { test, msg } => Ok(StmtKind::Assert { + test: Foldable::fold(test, folder)?, + msg: Foldable::fold(msg, folder)?, + }), + StmtKind::Import { names } => Ok(StmtKind::Import { + names: Foldable::fold(names, folder)?, + }), + StmtKind::ImportFrom { + module, + names, + level, + } => Ok(StmtKind::ImportFrom { + module: Foldable::fold(module, folder)?, + names: Foldable::fold(names, folder)?, + level: Foldable::fold(level, folder)?, + }), + StmtKind::Global { names } => Ok(StmtKind::Global { + names: Foldable::fold(names, folder)?, + }), + StmtKind::Nonlocal { names } => Ok(StmtKind::Nonlocal { + names: Foldable::fold(names, folder)?, + }), + StmtKind::Expr { value } => Ok(StmtKind::Expr { + value: Foldable::fold(value, folder)?, + }), + StmtKind::Pass {} => Ok(StmtKind::Pass {}), + StmtKind::Break {} => Ok(StmtKind::Break {}), + StmtKind::Continue {} => Ok(StmtKind::Continue {}), + }) + } + impl Foldable for Expr { + type Mapped = Expr; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_expr(self) + } + } + pub fn fold_expr + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Expr, + ) -> Result, F::Error> { + fold_located(folder, node, |folder, node| match node { + ExprKind::BoolOp { op, values } => Ok(ExprKind::BoolOp { + op: Foldable::fold(op, folder)?, + values: Foldable::fold(values, folder)?, + }), + ExprKind::NamedExpr { target, value } => Ok(ExprKind::NamedExpr { + target: Foldable::fold(target, folder)?, + value: Foldable::fold(value, folder)?, + }), + ExprKind::BinOp { left, op, right } => Ok(ExprKind::BinOp { + left: Foldable::fold(left, folder)?, + op: Foldable::fold(op, folder)?, + right: Foldable::fold(right, folder)?, + }), + ExprKind::UnaryOp { op, operand } => Ok(ExprKind::UnaryOp { + op: Foldable::fold(op, folder)?, + operand: Foldable::fold(operand, folder)?, + }), + ExprKind::Lambda { args, body } => Ok(ExprKind::Lambda { + args: Foldable::fold(args, folder)?, + body: Foldable::fold(body, folder)?, + }), + ExprKind::IfExp { test, body, orelse } => Ok(ExprKind::IfExp { + test: Foldable::fold(test, folder)?, + body: Foldable::fold(body, folder)?, + orelse: Foldable::fold(orelse, folder)?, + }), + ExprKind::Dict { keys, values } => Ok(ExprKind::Dict { + keys: Foldable::fold(keys, folder)?, + values: Foldable::fold(values, folder)?, + }), + ExprKind::Set { elts } => Ok(ExprKind::Set { + elts: Foldable::fold(elts, folder)?, + }), + ExprKind::ListComp { elt, generators } => Ok(ExprKind::ListComp { + elt: Foldable::fold(elt, folder)?, + generators: Foldable::fold(generators, folder)?, + }), + ExprKind::SetComp { elt, generators } => Ok(ExprKind::SetComp { + elt: Foldable::fold(elt, folder)?, + generators: Foldable::fold(generators, folder)?, + }), + ExprKind::DictComp { + key, + value, + generators, + } => Ok(ExprKind::DictComp { + key: Foldable::fold(key, folder)?, + value: Foldable::fold(value, folder)?, + generators: Foldable::fold(generators, folder)?, + }), + ExprKind::GeneratorExp { elt, generators } => Ok(ExprKind::GeneratorExp { + elt: Foldable::fold(elt, folder)?, + generators: Foldable::fold(generators, folder)?, + }), + ExprKind::Await { value } => Ok(ExprKind::Await { + value: Foldable::fold(value, folder)?, + }), + ExprKind::Yield { value } => Ok(ExprKind::Yield { + value: Foldable::fold(value, folder)?, + }), + ExprKind::YieldFrom { value } => Ok(ExprKind::YieldFrom { + value: Foldable::fold(value, folder)?, + }), + ExprKind::Compare { + left, + ops, + comparators, + } => Ok(ExprKind::Compare { + left: Foldable::fold(left, folder)?, + ops: Foldable::fold(ops, folder)?, + comparators: Foldable::fold(comparators, folder)?, + }), + ExprKind::Call { + func, + args, + keywords, + } => Ok(ExprKind::Call { + func: Foldable::fold(func, folder)?, + args: Foldable::fold(args, folder)?, + keywords: Foldable::fold(keywords, folder)?, + }), + ExprKind::FormattedValue { + value, + conversion, + format_spec, + } => Ok(ExprKind::FormattedValue { + value: Foldable::fold(value, folder)?, + conversion: Foldable::fold(conversion, folder)?, + format_spec: Foldable::fold(format_spec, folder)?, + }), + ExprKind::JoinedStr { values } => Ok(ExprKind::JoinedStr { + values: Foldable::fold(values, folder)?, + }), + ExprKind::Constant { value, kind } => Ok(ExprKind::Constant { + value: Foldable::fold(value, folder)?, + kind: Foldable::fold(kind, folder)?, + }), + ExprKind::Attribute { value, attr, ctx } => Ok(ExprKind::Attribute { + value: Foldable::fold(value, folder)?, + attr: Foldable::fold(attr, folder)?, + ctx: Foldable::fold(ctx, folder)?, + }), + ExprKind::Subscript { value, slice, ctx } => Ok(ExprKind::Subscript { + value: Foldable::fold(value, folder)?, + slice: Foldable::fold(slice, folder)?, + ctx: Foldable::fold(ctx, folder)?, + }), + ExprKind::Starred { value, ctx } => Ok(ExprKind::Starred { + value: Foldable::fold(value, folder)?, + ctx: Foldable::fold(ctx, folder)?, + }), + ExprKind::Name { id, ctx } => Ok(ExprKind::Name { + id: Foldable::fold(id, folder)?, + ctx: Foldable::fold(ctx, folder)?, + }), + ExprKind::List { elts, ctx } => Ok(ExprKind::List { + elts: Foldable::fold(elts, folder)?, + ctx: Foldable::fold(ctx, folder)?, + }), + ExprKind::Tuple { elts, ctx } => Ok(ExprKind::Tuple { + elts: Foldable::fold(elts, folder)?, + ctx: Foldable::fold(ctx, folder)?, + }), + ExprKind::Slice { lower, upper, step } => Ok(ExprKind::Slice { + lower: Foldable::fold(lower, folder)?, + upper: Foldable::fold(upper, folder)?, + step: Foldable::fold(step, folder)?, + }), + }) + } + impl Foldable for ExprContext { + type Mapped = ExprContext; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_expr_context(self) + } + } + pub fn fold_expr_context + ?Sized>( + #[allow(unused)] folder: &mut F, + node: ExprContext, + ) -> Result { + match node { + ExprContext::Load {} => Ok(ExprContext::Load {}), + ExprContext::Store {} => Ok(ExprContext::Store {}), + ExprContext::Del {} => Ok(ExprContext::Del {}), + } + } + impl Foldable for Boolop { + type Mapped = Boolop; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_boolop(self) + } + } + pub fn fold_boolop + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Boolop, + ) -> Result { + match node { + Boolop::And {} => Ok(Boolop::And {}), + Boolop::Or {} => Ok(Boolop::Or {}), + } + } + impl Foldable for Operator { + type Mapped = Operator; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_operator(self) + } + } + pub fn fold_operator + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Operator, + ) -> Result { + match node { + Operator::Add {} => Ok(Operator::Add {}), + Operator::Sub {} => Ok(Operator::Sub {}), + Operator::Mult {} => Ok(Operator::Mult {}), + Operator::MatMult {} => Ok(Operator::MatMult {}), + Operator::Div {} => Ok(Operator::Div {}), + Operator::Mod {} => Ok(Operator::Mod {}), + Operator::Pow {} => Ok(Operator::Pow {}), + Operator::LShift {} => Ok(Operator::LShift {}), + Operator::RShift {} => Ok(Operator::RShift {}), + Operator::BitOr {} => Ok(Operator::BitOr {}), + Operator::BitXor {} => Ok(Operator::BitXor {}), + Operator::BitAnd {} => Ok(Operator::BitAnd {}), + Operator::FloorDiv {} => Ok(Operator::FloorDiv {}), + } + } + impl Foldable for Unaryop { + type Mapped = Unaryop; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_unaryop(self) + } + } + pub fn fold_unaryop + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Unaryop, + ) -> Result { + match node { + Unaryop::Invert {} => Ok(Unaryop::Invert {}), + Unaryop::Not {} => Ok(Unaryop::Not {}), + Unaryop::UAdd {} => Ok(Unaryop::UAdd {}), + Unaryop::USub {} => Ok(Unaryop::USub {}), + } + } + impl Foldable for Cmpop { + type Mapped = Cmpop; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_cmpop(self) + } + } + pub fn fold_cmpop + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Cmpop, + ) -> Result { + match node { + Cmpop::Eq {} => Ok(Cmpop::Eq {}), + Cmpop::NotEq {} => Ok(Cmpop::NotEq {}), + Cmpop::Lt {} => Ok(Cmpop::Lt {}), + Cmpop::LtE {} => Ok(Cmpop::LtE {}), + Cmpop::Gt {} => Ok(Cmpop::Gt {}), + Cmpop::GtE {} => Ok(Cmpop::GtE {}), + Cmpop::Is {} => Ok(Cmpop::Is {}), + Cmpop::IsNot {} => Ok(Cmpop::IsNot {}), + Cmpop::In {} => Ok(Cmpop::In {}), + Cmpop::NotIn {} => Ok(Cmpop::NotIn {}), + } + } + impl Foldable for Comprehension { + type Mapped = Comprehension; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_comprehension(self) + } + } + pub fn fold_comprehension + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Comprehension, + ) -> Result, F::Error> { + let Comprehension { + target, + iter, + ifs, + is_async, + } = node; + Ok(Comprehension { + target: Foldable::fold(target, folder)?, + iter: Foldable::fold(iter, folder)?, + ifs: Foldable::fold(ifs, folder)?, + is_async: Foldable::fold(is_async, folder)?, + }) + } + impl Foldable for Excepthandler { + type Mapped = Excepthandler; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_excepthandler(self) + } + } + pub fn fold_excepthandler + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Excepthandler, + ) -> Result, F::Error> { + fold_located(folder, node, |folder, node| match node { + ExcepthandlerKind::ExceptHandler { type_, name, body } => { + Ok(ExcepthandlerKind::ExceptHandler { + type_: Foldable::fold(type_, folder)?, + name: Foldable::fold(name, folder)?, + body: Foldable::fold(body, folder)?, + }) + } + }) + } + impl Foldable for Arguments { + type Mapped = Arguments; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_arguments(self) + } + } + pub fn fold_arguments + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Arguments, + ) -> Result, F::Error> { + let Arguments { + posonlyargs, + args, + vararg, + kwonlyargs, + kw_defaults, + kwarg, + defaults, + } = node; + Ok(Arguments { + posonlyargs: Foldable::fold(posonlyargs, folder)?, + args: Foldable::fold(args, folder)?, + vararg: Foldable::fold(vararg, folder)?, + kwonlyargs: Foldable::fold(kwonlyargs, folder)?, + kw_defaults: Foldable::fold(kw_defaults, folder)?, + kwarg: Foldable::fold(kwarg, folder)?, + defaults: Foldable::fold(defaults, folder)?, + }) + } + impl Foldable for Arg { + type Mapped = Arg; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_arg(self) + } + } + pub fn fold_arg + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Arg, + ) -> Result, F::Error> { + fold_located(folder, node, |folder, node| { + let ArgData { + arg, + annotation, + type_comment, + } = node; + Ok(ArgData { + arg: Foldable::fold(arg, folder)?, + annotation: Foldable::fold(annotation, folder)?, + type_comment: Foldable::fold(type_comment, folder)?, + }) + }) + } + impl Foldable for Keyword { + type Mapped = Keyword; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_keyword(self) + } + } + pub fn fold_keyword + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Keyword, + ) -> Result, F::Error> { + fold_located(folder, node, |folder, node| { + let KeywordData { arg, value } = node; + Ok(KeywordData { + arg: Foldable::fold(arg, folder)?, + value: Foldable::fold(value, folder)?, + }) + }) + } + impl Foldable for Alias { + type Mapped = Alias; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_alias(self) + } + } + pub fn fold_alias + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Alias, + ) -> Result, F::Error> { + fold_located(folder, node, |folder, node| { + let AliasData { name, asname } = node; + Ok(AliasData { + name: Foldable::fold(name, folder)?, + asname: Foldable::fold(asname, folder)?, + }) + }) + } + impl Foldable for Withitem { + type Mapped = Withitem; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_withitem(self) + } + } + pub fn fold_withitem + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Withitem, + ) -> Result, F::Error> { + let Withitem { + context_expr, + optional_vars, + } = node; + Ok(Withitem { + context_expr: Foldable::fold(context_expr, folder)?, + optional_vars: Foldable::fold(optional_vars, folder)?, + }) + } + impl Foldable for MatchCase { + type Mapped = MatchCase; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_match_case(self) + } + } + pub fn fold_match_case + ?Sized>( + #[allow(unused)] folder: &mut F, + node: MatchCase, + ) -> Result, F::Error> { + let MatchCase { + pattern, + guard, + body, + } = node; + Ok(MatchCase { + pattern: Foldable::fold(pattern, folder)?, + guard: Foldable::fold(guard, folder)?, + body: Foldable::fold(body, folder)?, + }) + } + impl Foldable for Pattern { + type Mapped = Pattern; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_pattern(self) + } + } + pub fn fold_pattern + ?Sized>( + #[allow(unused)] folder: &mut F, + node: Pattern, + ) -> Result, F::Error> { + fold_located(folder, node, |folder, node| match node { + PatternKind::MatchValue { value } => Ok(PatternKind::MatchValue { + value: Foldable::fold(value, folder)?, + }), + PatternKind::MatchSingleton { value } => Ok(PatternKind::MatchSingleton { + value: Foldable::fold(value, folder)?, + }), + PatternKind::MatchSequence { patterns } => Ok(PatternKind::MatchSequence { + patterns: Foldable::fold(patterns, folder)?, + }), + PatternKind::MatchMapping { + keys, + patterns, + rest, + } => Ok(PatternKind::MatchMapping { + keys: Foldable::fold(keys, folder)?, + patterns: Foldable::fold(patterns, folder)?, + rest: Foldable::fold(rest, folder)?, + }), + PatternKind::MatchClass { + cls, + patterns, + kwd_attrs, + kwd_patterns, + } => Ok(PatternKind::MatchClass { + cls: Foldable::fold(cls, folder)?, + patterns: Foldable::fold(patterns, folder)?, + kwd_attrs: Foldable::fold(kwd_attrs, folder)?, + kwd_patterns: Foldable::fold(kwd_patterns, folder)?, + }), + PatternKind::MatchStar { name } => Ok(PatternKind::MatchStar { + name: Foldable::fold(name, folder)?, + }), + PatternKind::MatchAs { pattern, name } => Ok(PatternKind::MatchAs { + pattern: Foldable::fold(pattern, folder)?, + name: Foldable::fold(name, folder)?, + }), + PatternKind::MatchOr { patterns } => Ok(PatternKind::MatchOr { + patterns: Foldable::fold(patterns, folder)?, + }), + }) + } + impl Foldable for TypeIgnore { + type Mapped = TypeIgnore; + fn fold + ?Sized>( + self, + folder: &mut F, + ) -> Result { + folder.fold_type_ignore(self) + } + } + pub fn fold_type_ignore + ?Sized>( + #[allow(unused)] folder: &mut F, + node: TypeIgnore, + ) -> Result { + match node { + TypeIgnore::TypeIgnore { lineno, tag } => Ok(TypeIgnore::TypeIgnore { + lineno: Foldable::fold(lineno, folder)?, + tag: Foldable::fold(tag, folder)?, + }), + } + } +} diff --git a/ast/src/builtin.rs b/ast/src/builtin.rs deleted file mode 100644 index 47dcc7b1..00000000 --- a/ast/src/builtin.rs +++ /dev/null @@ -1,216 +0,0 @@ -//! `builtin_types` in asdl.py and Attributed - -use crate::bigint::BigInt; - -pub type String = std::string::String; - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Identifier(String); - -impl Identifier { - #[inline] - pub fn new(s: impl Into) -> Self { - Self(s.into()) - } -} - -impl Identifier { - #[inline] - pub fn as_str(&self) -> &str { - self.0.as_str() - } -} - -impl std::cmp::PartialEq for Identifier { - #[inline] - fn eq(&self, other: &str) -> bool { - self.0 == other - } -} - -impl std::cmp::PartialEq for Identifier { - #[inline] - fn eq(&self, other: &String) -> bool { - &self.0 == other - } -} - -impl std::ops::Deref for Identifier { - type Target = str; - #[inline] - fn deref(&self) -> &Self::Target { - self.0.as_str() - } -} - -impl AsRef for Identifier { - #[inline] - fn as_ref(&self) -> &str { - self.0.as_str() - } -} - -impl AsRef for Identifier { - #[inline] - fn as_ref(&self) -> &String { - &self.0 - } -} - -impl std::fmt::Display for Identifier { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl From for String { - #[inline] - fn from(id: Identifier) -> String { - id.0 - } -} - -impl From for Identifier { - #[inline] - fn from(id: String) -> Self { - Self(id) - } -} - -impl<'a> From<&'a str> for Identifier { - #[inline] - fn from(id: &'a str) -> Identifier { - id.to_owned().into() - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Int(u32); - -impl Int { - pub fn new(i: u32) -> Self { - Self(i) - } - pub fn to_u32(&self) -> u32 { - self.0 - } - pub fn to_usize(&self) -> usize { - self.0 as _ - } -} - -impl std::cmp::PartialEq for Int { - #[inline] - fn eq(&self, other: &u32) -> bool { - self.0 == *other - } -} - -impl std::cmp::PartialEq for Int { - #[inline] - fn eq(&self, other: &usize) -> bool { - self.0 as usize == *other - } -} - -#[derive(Clone, Debug, PartialEq, is_macro::Is)] -pub enum Constant { - None, - Bool(bool), - Str(String), - Bytes(Vec), - Int(BigInt), - Tuple(Vec), - Float(f64), - Complex { real: f64, imag: f64 }, - Ellipsis, -} - -impl Constant { - pub fn is_true(self) -> bool { - self.bool().is_some_and(|b| b) - } - pub fn is_false(self) -> bool { - self.bool().is_some_and(|b| !b) - } - pub fn complex(self) -> Option<(f64, f64)> { - match self { - Constant::Complex { real, imag } => Some((real, imag)), - _ => None, - } - } -} - -impl From for Constant { - fn from(s: String) -> Constant { - Self::Str(s) - } -} -impl From> for Constant { - fn from(b: Vec) -> Constant { - Self::Bytes(b) - } -} -impl From for Constant { - fn from(b: bool) -> Constant { - Self::Bool(b) - } -} -impl From for Constant { - fn from(i: BigInt) -> Constant { - Self::Int(i) - } -} - -#[cfg(feature = "rustpython-literal")] -impl std::fmt::Display for Constant { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Constant::None => f.pad("None"), - Constant::Bool(b) => f.pad(if *b { "True" } else { "False" }), - Constant::Str(s) => rustpython_literal::escape::UnicodeEscape::new_repr(s.as_str()) - .str_repr() - .write(f), - Constant::Bytes(b) => { - let escape = rustpython_literal::escape::AsciiEscape::new_repr(b); - let repr = escape.bytes_repr().to_string().unwrap(); - f.pad(&repr) - } - Constant::Int(i) => i.fmt(f), - Constant::Tuple(tup) => { - if let [elt] = &**tup { - write!(f, "({elt},)") - } else { - f.write_str("(")?; - for (i, elt) in tup.iter().enumerate() { - if i != 0 { - f.write_str(", ")?; - } - elt.fmt(f)?; - } - f.write_str(")") - } - } - Constant::Float(fp) => f.pad(&rustpython_literal::float::to_string(*fp)), - Constant::Complex { real, imag } => { - if *real == 0.0 { - write!(f, "{imag}j") - } else { - write!(f, "({real}{imag:+}j)") - } - } - Constant::Ellipsis => f.pad("..."), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn test_is_macro() { - let none = Constant::None; - assert!(none.is_none()); - assert!(!none.is_bool()); - } -} diff --git a/ast/src/constant.rs b/ast/src/constant.rs new file mode 100644 index 00000000..3514e521 --- /dev/null +++ b/ast/src/constant.rs @@ -0,0 +1,239 @@ +use num_bigint::BigInt; +pub use rustpython_compiler_core::ConversionFlag; + +#[derive(Clone, Debug, PartialEq)] +pub enum Constant { + None, + Bool(bool), + Str(String), + Bytes(Vec), + Int(BigInt), + Tuple(Vec), + Float(f64), + Complex { real: f64, imag: f64 }, + Ellipsis, +} + +impl From for Constant { + fn from(s: String) -> Constant { + Self::Str(s) + } +} +impl From> for Constant { + fn from(b: Vec) -> Constant { + Self::Bytes(b) + } +} +impl From for Constant { + fn from(b: bool) -> Constant { + Self::Bool(b) + } +} +impl From for Constant { + fn from(i: BigInt) -> Constant { + Self::Int(i) + } +} + +#[cfg(feature = "rustpython-literal")] +impl std::fmt::Display for Constant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Constant::None => f.pad("None"), + Constant::Bool(b) => f.pad(if *b { "True" } else { "False" }), + Constant::Str(s) => rustpython_literal::escape::UnicodeEscape::new_repr(s.as_str()) + .str_repr() + .write(f), + Constant::Bytes(b) => { + let escape = rustpython_literal::escape::AsciiEscape::new_repr(b); + let repr = escape.bytes_repr().to_string().unwrap(); + f.pad(&repr) + } + Constant::Int(i) => i.fmt(f), + Constant::Tuple(tup) => { + if let [elt] = &**tup { + write!(f, "({elt},)") + } else { + f.write_str("(")?; + for (i, elt) in tup.iter().enumerate() { + if i != 0 { + f.write_str(", ")?; + } + elt.fmt(f)?; + } + f.write_str(")") + } + } + Constant::Float(fp) => f.pad(&rustpython_literal::float::to_string(*fp)), + Constant::Complex { real, imag } => { + if *real == 0.0 { + write!(f, "{imag}j") + } else { + write!(f, "({real}{imag:+}j)") + } + } + Constant::Ellipsis => f.pad("..."), + } + } +} + +#[cfg(feature = "constant-optimization")] +#[non_exhaustive] +#[derive(Default)] +pub struct ConstantOptimizer {} + +#[cfg(feature = "constant-optimization")] +impl ConstantOptimizer { + #[inline] + pub fn new() -> Self { + Self {} + } +} + +#[cfg(feature = "constant-optimization")] +impl crate::fold::Fold for ConstantOptimizer { + type TargetU = U; + type Error = std::convert::Infallible; + #[inline] + fn map_user(&mut self, user: U) -> Result { + Ok(user) + } + fn fold_expr(&mut self, node: crate::Expr) -> Result, Self::Error> { + match node.node { + crate::ExprKind::Tuple { elts, ctx } => { + let elts = elts + .into_iter() + .map(|x| self.fold_expr(x)) + .collect::, _>>()?; + let expr = if elts + .iter() + .all(|e| matches!(e.node, crate::ExprKind::Constant { .. })) + { + let tuple = elts + .into_iter() + .map(|e| match e.node { + crate::ExprKind::Constant { value, .. } => value, + _ => unreachable!(), + }) + .collect(); + crate::ExprKind::Constant { + value: Constant::Tuple(tuple), + kind: None, + } + } else { + crate::ExprKind::Tuple { elts, ctx } + }; + Ok(crate::Expr { + node: expr, + custom: node.custom, + location: node.location, + end_location: node.end_location, + }) + } + _ => crate::fold::fold_expr(self, node), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "constant-optimization")] + #[test] + fn test_constant_opt() { + use crate::{fold::Fold, *}; + + let start = Default::default(); + let end = None; + let custom = (); + let ast = Located { + location: start, + end_location: end, + custom, + node: ExprKind::Tuple { + ctx: ExprContext::Load, + elts: vec![ + Located { + location: start, + end_location: end, + custom, + node: ExprKind::Constant { + value: BigInt::from(1).into(), + kind: None, + }, + }, + Located { + location: start, + end_location: end, + custom, + node: ExprKind::Constant { + value: BigInt::from(2).into(), + kind: None, + }, + }, + Located { + location: start, + end_location: end, + custom, + node: ExprKind::Tuple { + ctx: ExprContext::Load, + elts: vec![ + Located { + location: start, + end_location: end, + custom, + node: ExprKind::Constant { + value: BigInt::from(3).into(), + kind: None, + }, + }, + Located { + location: start, + end_location: end, + custom, + node: ExprKind::Constant { + value: BigInt::from(4).into(), + kind: None, + }, + }, + Located { + location: start, + end_location: end, + custom, + node: ExprKind::Constant { + value: BigInt::from(5).into(), + kind: None, + }, + }, + ], + }, + }, + ], + }, + }; + let new_ast = ConstantOptimizer::new() + .fold_expr(ast) + .unwrap_or_else(|e| match e {}); + assert_eq!( + new_ast, + Located { + location: start, + end_location: end, + custom, + node: ExprKind::Constant { + value: Constant::Tuple(vec![ + BigInt::from(1).into(), + BigInt::from(2).into(), + Constant::Tuple(vec![ + BigInt::from(3).into(), + BigInt::from(4).into(), + BigInt::from(5).into(), + ]) + ]), + kind: None + }, + } + ); + } +} diff --git a/ast/src/fold.rs b/ast/src/fold_helpers.rs similarity index 81% rename from ast/src/fold.rs rename to ast/src/fold_helpers.rs index ccbcc7bc..969ea4e5 100644 --- a/ast/src/fold.rs +++ b/ast/src/fold_helpers.rs @@ -1,8 +1,6 @@ -use super::generic::*; +use crate::{constant, fold::Fold}; -use crate::{builtin, ConversionFlag}; - -pub trait Foldable { +pub(crate) trait Foldable { type Mapped; fn fold + ?Sized>( self, @@ -51,7 +49,7 @@ where macro_rules! simple_fold { ($($t:ty),+$(,)?) => { - $(impl $crate::fold::Foldable for $t { + $(impl $crate::fold_helpers::Foldable for $t { type Mapped = Self; #[inline] fn fold + ?Sized>( @@ -64,13 +62,4 @@ macro_rules! simple_fold { }; } -simple_fold!( - builtin::Int, - builtin::String, - builtin::Identifier, - bool, - ConversionFlag, - builtin::Constant -); - -include!("gen/fold.rs"); +simple_fold!(usize, String, bool, constant::Constant); diff --git a/ast/src/gen/fold.rs b/ast/src/gen/fold.rs deleted file mode 100644 index b133b891..00000000 --- a/ast/src/gen/fold.rs +++ /dev/null @@ -1,2974 +0,0 @@ -// File automatically generated by ast/asdl_rs.py. - -pub trait Fold { - type TargetU; - type Error; - type UserContext; - - fn will_map_user(&mut self, user: &U) -> Self::UserContext; - #[cfg(feature = "all-nodes-with-ranges")] - fn will_map_user_cfg(&mut self, user: &U) -> Self::UserContext { - self.will_map_user(user) - } - #[cfg(not(feature = "all-nodes-with-ranges"))] - fn will_map_user_cfg( - &mut self, - _user: &crate::EmptyRange, - ) -> crate::EmptyRange { - crate::EmptyRange::default() - } - fn map_user( - &mut self, - user: U, - context: Self::UserContext, - ) -> Result; - #[cfg(feature = "all-nodes-with-ranges")] - fn map_user_cfg( - &mut self, - user: U, - context: Self::UserContext, - ) -> Result { - self.map_user(user, context) - } - #[cfg(not(feature = "all-nodes-with-ranges"))] - fn map_user_cfg( - &mut self, - _user: crate::EmptyRange, - _context: crate::EmptyRange, - ) -> Result, Self::Error> { - Ok(crate::EmptyRange::default()) - } - - fn fold>(&mut self, node: X) -> Result { - node.fold(self) - } - fn fold_mod(&mut self, node: Mod) -> Result, Self::Error> { - fold_mod(self, node) - } - fn fold_mod_module( - &mut self, - node: ModModule, - ) -> Result, Self::Error> { - fold_mod_module(self, node) - } - fn fold_mod_interactive( - &mut self, - node: ModInteractive, - ) -> Result, Self::Error> { - fold_mod_interactive(self, node) - } - fn fold_mod_expression( - &mut self, - node: ModExpression, - ) -> Result, Self::Error> { - fold_mod_expression(self, node) - } - fn fold_mod_function_type( - &mut self, - node: ModFunctionType, - ) -> Result, Self::Error> { - fold_mod_function_type(self, node) - } - fn fold_stmt(&mut self, node: Stmt) -> Result, Self::Error> { - fold_stmt(self, node) - } - fn fold_stmt_function_def( - &mut self, - node: StmtFunctionDef, - ) -> Result, Self::Error> { - fold_stmt_function_def(self, node) - } - fn fold_stmt_async_function_def( - &mut self, - node: StmtAsyncFunctionDef, - ) -> Result, Self::Error> { - fold_stmt_async_function_def(self, node) - } - fn fold_stmt_class_def( - &mut self, - node: StmtClassDef, - ) -> Result, Self::Error> { - fold_stmt_class_def(self, node) - } - fn fold_stmt_return( - &mut self, - node: StmtReturn, - ) -> Result, Self::Error> { - fold_stmt_return(self, node) - } - fn fold_stmt_delete( - &mut self, - node: StmtDelete, - ) -> Result, Self::Error> { - fold_stmt_delete(self, node) - } - fn fold_stmt_assign( - &mut self, - node: StmtAssign, - ) -> Result, Self::Error> { - fold_stmt_assign(self, node) - } - fn fold_stmt_type_alias( - &mut self, - node: StmtTypeAlias, - ) -> Result, Self::Error> { - fold_stmt_type_alias(self, node) - } - fn fold_stmt_aug_assign( - &mut self, - node: StmtAugAssign, - ) -> Result, Self::Error> { - fold_stmt_aug_assign(self, node) - } - fn fold_stmt_ann_assign( - &mut self, - node: StmtAnnAssign, - ) -> Result, Self::Error> { - fold_stmt_ann_assign(self, node) - } - fn fold_stmt_for(&mut self, node: StmtFor) -> Result, Self::Error> { - fold_stmt_for(self, node) - } - fn fold_stmt_async_for( - &mut self, - node: StmtAsyncFor, - ) -> Result, Self::Error> { - fold_stmt_async_for(self, node) - } - fn fold_stmt_while( - &mut self, - node: StmtWhile, - ) -> Result, Self::Error> { - fold_stmt_while(self, node) - } - fn fold_stmt_if(&mut self, node: StmtIf) -> Result, Self::Error> { - fold_stmt_if(self, node) - } - fn fold_stmt_with( - &mut self, - node: StmtWith, - ) -> Result, Self::Error> { - fold_stmt_with(self, node) - } - fn fold_stmt_async_with( - &mut self, - node: StmtAsyncWith, - ) -> Result, Self::Error> { - fold_stmt_async_with(self, node) - } - fn fold_stmt_match( - &mut self, - node: StmtMatch, - ) -> Result, Self::Error> { - fold_stmt_match(self, node) - } - fn fold_stmt_raise( - &mut self, - node: StmtRaise, - ) -> Result, Self::Error> { - fold_stmt_raise(self, node) - } - fn fold_stmt_try(&mut self, node: StmtTry) -> Result, Self::Error> { - fold_stmt_try(self, node) - } - fn fold_stmt_try_star( - &mut self, - node: StmtTryStar, - ) -> Result, Self::Error> { - fold_stmt_try_star(self, node) - } - fn fold_stmt_assert( - &mut self, - node: StmtAssert, - ) -> Result, Self::Error> { - fold_stmt_assert(self, node) - } - fn fold_stmt_import( - &mut self, - node: StmtImport, - ) -> Result, Self::Error> { - fold_stmt_import(self, node) - } - fn fold_stmt_import_from( - &mut self, - node: StmtImportFrom, - ) -> Result, Self::Error> { - fold_stmt_import_from(self, node) - } - fn fold_stmt_global( - &mut self, - node: StmtGlobal, - ) -> Result, Self::Error> { - fold_stmt_global(self, node) - } - fn fold_stmt_nonlocal( - &mut self, - node: StmtNonlocal, - ) -> Result, Self::Error> { - fold_stmt_nonlocal(self, node) - } - fn fold_stmt_expr( - &mut self, - node: StmtExpr, - ) -> Result, Self::Error> { - fold_stmt_expr(self, node) - } - fn fold_stmt_pass( - &mut self, - node: StmtPass, - ) -> Result, Self::Error> { - fold_stmt_pass(self, node) - } - fn fold_stmt_break( - &mut self, - node: StmtBreak, - ) -> Result, Self::Error> { - fold_stmt_break(self, node) - } - fn fold_stmt_continue( - &mut self, - node: StmtContinue, - ) -> Result, Self::Error> { - fold_stmt_continue(self, node) - } - fn fold_expr(&mut self, node: Expr) -> Result, Self::Error> { - fold_expr(self, node) - } - fn fold_expr_bool_op( - &mut self, - node: ExprBoolOp, - ) -> Result, Self::Error> { - fold_expr_bool_op(self, node) - } - fn fold_expr_named_expr( - &mut self, - node: ExprNamedExpr, - ) -> Result, Self::Error> { - fold_expr_named_expr(self, node) - } - fn fold_expr_bin_op( - &mut self, - node: ExprBinOp, - ) -> Result, Self::Error> { - fold_expr_bin_op(self, node) - } - fn fold_expr_unary_op( - &mut self, - node: ExprUnaryOp, - ) -> Result, Self::Error> { - fold_expr_unary_op(self, node) - } - fn fold_expr_lambda( - &mut self, - node: ExprLambda, - ) -> Result, Self::Error> { - fold_expr_lambda(self, node) - } - fn fold_expr_if_exp( - &mut self, - node: ExprIfExp, - ) -> Result, Self::Error> { - fold_expr_if_exp(self, node) - } - fn fold_expr_dict( - &mut self, - node: ExprDict, - ) -> Result, Self::Error> { - fold_expr_dict(self, node) - } - fn fold_expr_set(&mut self, node: ExprSet) -> Result, Self::Error> { - fold_expr_set(self, node) - } - fn fold_expr_list_comp( - &mut self, - node: ExprListComp, - ) -> Result, Self::Error> { - fold_expr_list_comp(self, node) - } - fn fold_expr_set_comp( - &mut self, - node: ExprSetComp, - ) -> Result, Self::Error> { - fold_expr_set_comp(self, node) - } - fn fold_expr_dict_comp( - &mut self, - node: ExprDictComp, - ) -> Result, Self::Error> { - fold_expr_dict_comp(self, node) - } - fn fold_expr_generator_exp( - &mut self, - node: ExprGeneratorExp, - ) -> Result, Self::Error> { - fold_expr_generator_exp(self, node) - } - fn fold_expr_await( - &mut self, - node: ExprAwait, - ) -> Result, Self::Error> { - fold_expr_await(self, node) - } - fn fold_expr_yield( - &mut self, - node: ExprYield, - ) -> Result, Self::Error> { - fold_expr_yield(self, node) - } - fn fold_expr_yield_from( - &mut self, - node: ExprYieldFrom, - ) -> Result, Self::Error> { - fold_expr_yield_from(self, node) - } - fn fold_expr_compare( - &mut self, - node: ExprCompare, - ) -> Result, Self::Error> { - fold_expr_compare(self, node) - } - fn fold_expr_call( - &mut self, - node: ExprCall, - ) -> Result, Self::Error> { - fold_expr_call(self, node) - } - fn fold_expr_formatted_value( - &mut self, - node: ExprFormattedValue, - ) -> Result, Self::Error> { - fold_expr_formatted_value(self, node) - } - fn fold_expr_joined_str( - &mut self, - node: ExprJoinedStr, - ) -> Result, Self::Error> { - fold_expr_joined_str(self, node) - } - fn fold_expr_constant( - &mut self, - node: ExprConstant, - ) -> Result, Self::Error> { - fold_expr_constant(self, node) - } - fn fold_expr_attribute( - &mut self, - node: ExprAttribute, - ) -> Result, Self::Error> { - fold_expr_attribute(self, node) - } - fn fold_expr_subscript( - &mut self, - node: ExprSubscript, - ) -> Result, Self::Error> { - fold_expr_subscript(self, node) - } - fn fold_expr_starred( - &mut self, - node: ExprStarred, - ) -> Result, Self::Error> { - fold_expr_starred(self, node) - } - fn fold_expr_name( - &mut self, - node: ExprName, - ) -> Result, Self::Error> { - fold_expr_name(self, node) - } - fn fold_expr_list( - &mut self, - node: ExprList, - ) -> Result, Self::Error> { - fold_expr_list(self, node) - } - fn fold_expr_tuple( - &mut self, - node: ExprTuple, - ) -> Result, Self::Error> { - fold_expr_tuple(self, node) - } - fn fold_expr_slice( - &mut self, - node: ExprSlice, - ) -> Result, Self::Error> { - fold_expr_slice(self, node) - } - fn fold_expr_context(&mut self, node: ExprContext) -> Result { - fold_expr_context(self, node) - } - fn fold_boolop(&mut self, node: BoolOp) -> Result { - fold_boolop(self, node) - } - fn fold_operator(&mut self, node: Operator) -> Result { - fold_operator(self, node) - } - fn fold_unaryop(&mut self, node: UnaryOp) -> Result { - fold_unaryop(self, node) - } - fn fold_cmpop(&mut self, node: CmpOp) -> Result { - fold_cmpop(self, node) - } - fn fold_comprehension( - &mut self, - node: Comprehension, - ) -> Result, Self::Error> { - fold_comprehension(self, node) - } - fn fold_excepthandler( - &mut self, - node: ExceptHandler, - ) -> Result, Self::Error> { - fold_excepthandler(self, node) - } - fn fold_excepthandler_except_handler( - &mut self, - node: ExceptHandlerExceptHandler, - ) -> Result, Self::Error> { - fold_excepthandler_except_handler(self, node) - } - fn fold_arguments( - &mut self, - node: Arguments, - ) -> Result, Self::Error> { - fold_arguments(self, node) - } - fn fold_arg(&mut self, node: Arg) -> Result, Self::Error> { - fold_arg(self, node) - } - fn fold_keyword(&mut self, node: Keyword) -> Result, Self::Error> { - fold_keyword(self, node) - } - fn fold_alias(&mut self, node: Alias) -> Result, Self::Error> { - fold_alias(self, node) - } - fn fold_withitem(&mut self, node: WithItem) -> Result, Self::Error> { - fold_withitem(self, node) - } - fn fold_match_case( - &mut self, - node: MatchCase, - ) -> Result, Self::Error> { - fold_match_case(self, node) - } - fn fold_pattern(&mut self, node: Pattern) -> Result, Self::Error> { - fold_pattern(self, node) - } - fn fold_pattern_match_value( - &mut self, - node: PatternMatchValue, - ) -> Result, Self::Error> { - fold_pattern_match_value(self, node) - } - fn fold_pattern_match_singleton( - &mut self, - node: PatternMatchSingleton, - ) -> Result, Self::Error> { - fold_pattern_match_singleton(self, node) - } - fn fold_pattern_match_sequence( - &mut self, - node: PatternMatchSequence, - ) -> Result, Self::Error> { - fold_pattern_match_sequence(self, node) - } - fn fold_pattern_match_mapping( - &mut self, - node: PatternMatchMapping, - ) -> Result, Self::Error> { - fold_pattern_match_mapping(self, node) - } - fn fold_pattern_match_class( - &mut self, - node: PatternMatchClass, - ) -> Result, Self::Error> { - fold_pattern_match_class(self, node) - } - fn fold_pattern_match_star( - &mut self, - node: PatternMatchStar, - ) -> Result, Self::Error> { - fold_pattern_match_star(self, node) - } - fn fold_pattern_match_as( - &mut self, - node: PatternMatchAs, - ) -> Result, Self::Error> { - fold_pattern_match_as(self, node) - } - fn fold_pattern_match_or( - &mut self, - node: PatternMatchOr, - ) -> Result, Self::Error> { - fold_pattern_match_or(self, node) - } - fn fold_type_ignore( - &mut self, - node: TypeIgnore, - ) -> Result, Self::Error> { - fold_type_ignore(self, node) - } - fn fold_type_ignore_type_ignore( - &mut self, - node: TypeIgnoreTypeIgnore, - ) -> Result, Self::Error> { - fold_type_ignore_type_ignore(self, node) - } - fn fold_type_param( - &mut self, - node: TypeParam, - ) -> Result, Self::Error> { - fold_type_param(self, node) - } - fn fold_type_param_type_var( - &mut self, - node: TypeParamTypeVar, - ) -> Result, Self::Error> { - fold_type_param_type_var(self, node) - } - fn fold_type_param_param_spec( - &mut self, - node: TypeParamParamSpec, - ) -> Result, Self::Error> { - fold_type_param_param_spec(self, node) - } - fn fold_type_param_type_var_tuple( - &mut self, - node: TypeParamTypeVarTuple, - ) -> Result, Self::Error> { - fold_type_param_type_var_tuple(self, node) - } - fn fold_arg_with_default( - &mut self, - node: ArgWithDefault, - ) -> Result, Self::Error> { - fold_arg_with_default(self, node) - } -} -impl Foldable for Mod { - type Mapped = Mod; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_mod(self) - } -} -pub fn fold_mod + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Mod, -) -> Result, F::Error> { - let folded = match node { - Mod::Module(cons) => Mod::Module(Foldable::fold(cons, folder)?), - Mod::Interactive(cons) => Mod::Interactive(Foldable::fold(cons, folder)?), - Mod::Expression(cons) => Mod::Expression(Foldable::fold(cons, folder)?), - Mod::FunctionType(cons) => Mod::FunctionType(Foldable::fold(cons, folder)?), - }; - Ok(folded) -} -impl Foldable for ModModule { - type Mapped = ModModule; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_mod_module(self) - } -} -pub fn fold_mod_module + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ModModule, -) -> Result, F::Error> { - let ModModule { - body, - type_ignores, - range, - } = node; - let context = folder.will_map_user_cfg(&range); - - let body = Foldable::fold(body, folder)?; - let type_ignores = Foldable::fold(type_ignores, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(ModModule { - body, - type_ignores, - range, - }) -} -impl Foldable for ModInteractive { - type Mapped = ModInteractive; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_mod_interactive(self) - } -} -pub fn fold_mod_interactive + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ModInteractive, -) -> Result, F::Error> { - let ModInteractive { body, range } = node; - let context = folder.will_map_user_cfg(&range); - - let body = Foldable::fold(body, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(ModInteractive { body, range }) -} -impl Foldable for ModExpression { - type Mapped = ModExpression; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_mod_expression(self) - } -} -pub fn fold_mod_expression + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ModExpression, -) -> Result, F::Error> { - let ModExpression { body, range } = node; - let context = folder.will_map_user_cfg(&range); - - let body = Foldable::fold(body, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(ModExpression { body, range }) -} -impl Foldable for ModFunctionType { - type Mapped = ModFunctionType; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_mod_function_type(self) - } -} -pub fn fold_mod_function_type + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ModFunctionType, -) -> Result, F::Error> { - let ModFunctionType { - argtypes, - returns, - range, - } = node; - let context = folder.will_map_user_cfg(&range); - - let argtypes = Foldable::fold(argtypes, folder)?; - let returns = Foldable::fold(returns, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(ModFunctionType { - argtypes, - returns, - range, - }) -} -impl Foldable for Stmt { - type Mapped = Stmt; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt(self) - } -} -pub fn fold_stmt + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Stmt, -) -> Result, F::Error> { - let folded = match node { - Stmt::FunctionDef(cons) => Stmt::FunctionDef(Foldable::fold(cons, folder)?), - Stmt::AsyncFunctionDef(cons) => Stmt::AsyncFunctionDef(Foldable::fold(cons, folder)?), - Stmt::ClassDef(cons) => Stmt::ClassDef(Foldable::fold(cons, folder)?), - Stmt::Return(cons) => Stmt::Return(Foldable::fold(cons, folder)?), - Stmt::Delete(cons) => Stmt::Delete(Foldable::fold(cons, folder)?), - Stmt::Assign(cons) => Stmt::Assign(Foldable::fold(cons, folder)?), - Stmt::TypeAlias(cons) => Stmt::TypeAlias(Foldable::fold(cons, folder)?), - Stmt::AugAssign(cons) => Stmt::AugAssign(Foldable::fold(cons, folder)?), - Stmt::AnnAssign(cons) => Stmt::AnnAssign(Foldable::fold(cons, folder)?), - Stmt::For(cons) => Stmt::For(Foldable::fold(cons, folder)?), - Stmt::AsyncFor(cons) => Stmt::AsyncFor(Foldable::fold(cons, folder)?), - Stmt::While(cons) => Stmt::While(Foldable::fold(cons, folder)?), - Stmt::If(cons) => Stmt::If(Foldable::fold(cons, folder)?), - Stmt::With(cons) => Stmt::With(Foldable::fold(cons, folder)?), - Stmt::AsyncWith(cons) => Stmt::AsyncWith(Foldable::fold(cons, folder)?), - Stmt::Match(cons) => Stmt::Match(Foldable::fold(cons, folder)?), - Stmt::Raise(cons) => Stmt::Raise(Foldable::fold(cons, folder)?), - Stmt::Try(cons) => Stmt::Try(Foldable::fold(cons, folder)?), - Stmt::TryStar(cons) => Stmt::TryStar(Foldable::fold(cons, folder)?), - Stmt::Assert(cons) => Stmt::Assert(Foldable::fold(cons, folder)?), - Stmt::Import(cons) => Stmt::Import(Foldable::fold(cons, folder)?), - Stmt::ImportFrom(cons) => Stmt::ImportFrom(Foldable::fold(cons, folder)?), - Stmt::Global(cons) => Stmt::Global(Foldable::fold(cons, folder)?), - Stmt::Nonlocal(cons) => Stmt::Nonlocal(Foldable::fold(cons, folder)?), - Stmt::Expr(cons) => Stmt::Expr(Foldable::fold(cons, folder)?), - Stmt::Pass(cons) => Stmt::Pass(Foldable::fold(cons, folder)?), - Stmt::Break(cons) => Stmt::Break(Foldable::fold(cons, folder)?), - Stmt::Continue(cons) => Stmt::Continue(Foldable::fold(cons, folder)?), - }; - Ok(folded) -} -impl Foldable for StmtFunctionDef { - type Mapped = StmtFunctionDef; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_function_def(self) - } -} -pub fn fold_stmt_function_def + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtFunctionDef, -) -> Result, F::Error> { - let StmtFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_comment, - type_params, - range, - } = node; - let context = folder.will_map_user(&range); - - let name = Foldable::fold(name, folder)?; - let args = Foldable::fold(args, folder)?; - let body = Foldable::fold(body, folder)?; - let decorator_list = Foldable::fold(decorator_list, folder)?; - let returns = Foldable::fold(returns, folder)?; - let type_comment = Foldable::fold(type_comment, folder)?; - let type_params = Foldable::fold(type_params, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_comment, - type_params, - range, - }) -} -impl Foldable for StmtAsyncFunctionDef { - type Mapped = StmtAsyncFunctionDef; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_async_function_def(self) - } -} -pub fn fold_stmt_async_function_def + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtAsyncFunctionDef, -) -> Result, F::Error> { - let StmtAsyncFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_comment, - type_params, - range, - } = node; - let context = folder.will_map_user(&range); - - let name = Foldable::fold(name, folder)?; - let args = Foldable::fold(args, folder)?; - let body = Foldable::fold(body, folder)?; - let decorator_list = Foldable::fold(decorator_list, folder)?; - let returns = Foldable::fold(returns, folder)?; - let type_comment = Foldable::fold(type_comment, folder)?; - let type_params = Foldable::fold(type_params, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtAsyncFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_comment, - type_params, - range, - }) -} -impl Foldable for StmtClassDef { - type Mapped = StmtClassDef; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_class_def(self) - } -} -pub fn fold_stmt_class_def + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtClassDef, -) -> Result, F::Error> { - let StmtClassDef { - name, - bases, - keywords, - body, - decorator_list, - type_params, - range, - } = node; - let context = folder.will_map_user(&range); - - let name = Foldable::fold(name, folder)?; - let bases = Foldable::fold(bases, folder)?; - let keywords = Foldable::fold(keywords, folder)?; - let body = Foldable::fold(body, folder)?; - let decorator_list = Foldable::fold(decorator_list, folder)?; - let type_params = Foldable::fold(type_params, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtClassDef { - name, - bases, - keywords, - body, - decorator_list, - type_params, - range, - }) -} -impl Foldable for StmtReturn { - type Mapped = StmtReturn; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_return(self) - } -} -pub fn fold_stmt_return + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtReturn, -) -> Result, F::Error> { - let StmtReturn { value, range } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtReturn { value, range }) -} -impl Foldable for StmtDelete { - type Mapped = StmtDelete; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_delete(self) - } -} -pub fn fold_stmt_delete + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtDelete, -) -> Result, F::Error> { - let StmtDelete { targets, range } = node; - let context = folder.will_map_user(&range); - - let targets = Foldable::fold(targets, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtDelete { targets, range }) -} -impl Foldable for StmtAssign { - type Mapped = StmtAssign; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_assign(self) - } -} -pub fn fold_stmt_assign + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtAssign, -) -> Result, F::Error> { - let StmtAssign { - targets, - value, - type_comment, - range, - } = node; - let context = folder.will_map_user(&range); - - let targets = Foldable::fold(targets, folder)?; - let value = Foldable::fold(value, folder)?; - let type_comment = Foldable::fold(type_comment, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtAssign { - targets, - value, - type_comment, - range, - }) -} -impl Foldable for StmtTypeAlias { - type Mapped = StmtTypeAlias; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_type_alias(self) - } -} -pub fn fold_stmt_type_alias + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtTypeAlias, -) -> Result, F::Error> { - let StmtTypeAlias { - name, - type_params, - value, - range, - } = node; - let context = folder.will_map_user(&range); - - let name = Foldable::fold(name, folder)?; - let type_params = Foldable::fold(type_params, folder)?; - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtTypeAlias { - name, - type_params, - value, - range, - }) -} -impl Foldable for StmtAugAssign { - type Mapped = StmtAugAssign; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_aug_assign(self) - } -} -pub fn fold_stmt_aug_assign + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtAugAssign, -) -> Result, F::Error> { - let StmtAugAssign { - target, - op, - value, - range, - } = node; - let context = folder.will_map_user(&range); - - let target = Foldable::fold(target, folder)?; - let op = Foldable::fold(op, folder)?; - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtAugAssign { - target, - op, - value, - range, - }) -} -impl Foldable for StmtAnnAssign { - type Mapped = StmtAnnAssign; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_ann_assign(self) - } -} -pub fn fold_stmt_ann_assign + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtAnnAssign, -) -> Result, F::Error> { - let StmtAnnAssign { - target, - annotation, - value, - simple, - range, - } = node; - let context = folder.will_map_user(&range); - - let target = Foldable::fold(target, folder)?; - let annotation = Foldable::fold(annotation, folder)?; - let value = Foldable::fold(value, folder)?; - let simple = Foldable::fold(simple, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtAnnAssign { - target, - annotation, - value, - simple, - range, - }) -} -impl Foldable for StmtFor { - type Mapped = StmtFor; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_for(self) - } -} -pub fn fold_stmt_for + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtFor, -) -> Result, F::Error> { - let StmtFor { - target, - iter, - body, - orelse, - type_comment, - range, - } = node; - let context = folder.will_map_user(&range); - - let target = Foldable::fold(target, folder)?; - let iter = Foldable::fold(iter, folder)?; - let body = Foldable::fold(body, folder)?; - let orelse = Foldable::fold(orelse, folder)?; - let type_comment = Foldable::fold(type_comment, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtFor { - target, - iter, - body, - orelse, - type_comment, - range, - }) -} -impl Foldable for StmtAsyncFor { - type Mapped = StmtAsyncFor; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_async_for(self) - } -} -pub fn fold_stmt_async_for + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtAsyncFor, -) -> Result, F::Error> { - let StmtAsyncFor { - target, - iter, - body, - orelse, - type_comment, - range, - } = node; - let context = folder.will_map_user(&range); - - let target = Foldable::fold(target, folder)?; - let iter = Foldable::fold(iter, folder)?; - let body = Foldable::fold(body, folder)?; - let orelse = Foldable::fold(orelse, folder)?; - let type_comment = Foldable::fold(type_comment, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtAsyncFor { - target, - iter, - body, - orelse, - type_comment, - range, - }) -} -impl Foldable for StmtWhile { - type Mapped = StmtWhile; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_while(self) - } -} -pub fn fold_stmt_while + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtWhile, -) -> Result, F::Error> { - let StmtWhile { - test, - body, - orelse, - range, - } = node; - let context = folder.will_map_user(&range); - - let test = Foldable::fold(test, folder)?; - let body = Foldable::fold(body, folder)?; - let orelse = Foldable::fold(orelse, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtWhile { - test, - body, - orelse, - range, - }) -} -impl Foldable for StmtIf { - type Mapped = StmtIf; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_if(self) - } -} -pub fn fold_stmt_if + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtIf, -) -> Result, F::Error> { - let StmtIf { - test, - body, - orelse, - range, - } = node; - let context = folder.will_map_user(&range); - - let test = Foldable::fold(test, folder)?; - let body = Foldable::fold(body, folder)?; - let orelse = Foldable::fold(orelse, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtIf { - test, - body, - orelse, - range, - }) -} -impl Foldable for StmtWith { - type Mapped = StmtWith; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_with(self) - } -} -pub fn fold_stmt_with + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtWith, -) -> Result, F::Error> { - let StmtWith { - items, - body, - type_comment, - range, - } = node; - let context = folder.will_map_user(&range); - - let items = Foldable::fold(items, folder)?; - let body = Foldable::fold(body, folder)?; - let type_comment = Foldable::fold(type_comment, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtWith { - items, - body, - type_comment, - range, - }) -} -impl Foldable for StmtAsyncWith { - type Mapped = StmtAsyncWith; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_async_with(self) - } -} -pub fn fold_stmt_async_with + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtAsyncWith, -) -> Result, F::Error> { - let StmtAsyncWith { - items, - body, - type_comment, - range, - } = node; - let context = folder.will_map_user(&range); - - let items = Foldable::fold(items, folder)?; - let body = Foldable::fold(body, folder)?; - let type_comment = Foldable::fold(type_comment, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtAsyncWith { - items, - body, - type_comment, - range, - }) -} -impl Foldable for StmtMatch { - type Mapped = StmtMatch; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_match(self) - } -} -pub fn fold_stmt_match + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtMatch, -) -> Result, F::Error> { - let StmtMatch { - subject, - cases, - range, - } = node; - let context = folder.will_map_user(&range); - - let subject = Foldable::fold(subject, folder)?; - let cases = Foldable::fold(cases, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtMatch { - subject, - cases, - range, - }) -} -impl Foldable for StmtRaise { - type Mapped = StmtRaise; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_raise(self) - } -} -pub fn fold_stmt_raise + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtRaise, -) -> Result, F::Error> { - let StmtRaise { exc, cause, range } = node; - let context = folder.will_map_user(&range); - - let exc = Foldable::fold(exc, folder)?; - let cause = Foldable::fold(cause, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtRaise { exc, cause, range }) -} -impl Foldable for StmtTry { - type Mapped = StmtTry; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_try(self) - } -} -pub fn fold_stmt_try + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtTry, -) -> Result, F::Error> { - let StmtTry { - body, - handlers, - orelse, - finalbody, - range, - } = node; - let context = folder.will_map_user(&range); - - let body = Foldable::fold(body, folder)?; - let handlers = Foldable::fold(handlers, folder)?; - let orelse = Foldable::fold(orelse, folder)?; - let finalbody = Foldable::fold(finalbody, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtTry { - body, - handlers, - orelse, - finalbody, - range, - }) -} -impl Foldable for StmtTryStar { - type Mapped = StmtTryStar; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_try_star(self) - } -} -pub fn fold_stmt_try_star + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtTryStar, -) -> Result, F::Error> { - let StmtTryStar { - body, - handlers, - orelse, - finalbody, - range, - } = node; - let context = folder.will_map_user(&range); - - let body = Foldable::fold(body, folder)?; - let handlers = Foldable::fold(handlers, folder)?; - let orelse = Foldable::fold(orelse, folder)?; - let finalbody = Foldable::fold(finalbody, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtTryStar { - body, - handlers, - orelse, - finalbody, - range, - }) -} -impl Foldable for StmtAssert { - type Mapped = StmtAssert; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_assert(self) - } -} -pub fn fold_stmt_assert + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtAssert, -) -> Result, F::Error> { - let StmtAssert { test, msg, range } = node; - let context = folder.will_map_user(&range); - - let test = Foldable::fold(test, folder)?; - let msg = Foldable::fold(msg, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtAssert { test, msg, range }) -} -impl Foldable for StmtImport { - type Mapped = StmtImport; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_import(self) - } -} -pub fn fold_stmt_import + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtImport, -) -> Result, F::Error> { - let StmtImport { names, range } = node; - let context = folder.will_map_user(&range); - - let names = Foldable::fold(names, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtImport { names, range }) -} -impl Foldable for StmtImportFrom { - type Mapped = StmtImportFrom; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_import_from(self) - } -} -pub fn fold_stmt_import_from + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtImportFrom, -) -> Result, F::Error> { - let StmtImportFrom { - module, - names, - level, - range, - } = node; - let context = folder.will_map_user(&range); - - let module = Foldable::fold(module, folder)?; - let names = Foldable::fold(names, folder)?; - let level = Foldable::fold(level, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtImportFrom { - module, - names, - level, - range, - }) -} -impl Foldable for StmtGlobal { - type Mapped = StmtGlobal; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_global(self) - } -} -pub fn fold_stmt_global + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtGlobal, -) -> Result, F::Error> { - let StmtGlobal { names, range } = node; - let context = folder.will_map_user(&range); - - let names = Foldable::fold(names, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtGlobal { names, range }) -} -impl Foldable for StmtNonlocal { - type Mapped = StmtNonlocal; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_nonlocal(self) - } -} -pub fn fold_stmt_nonlocal + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtNonlocal, -) -> Result, F::Error> { - let StmtNonlocal { names, range } = node; - let context = folder.will_map_user(&range); - - let names = Foldable::fold(names, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtNonlocal { names, range }) -} -impl Foldable for StmtExpr { - type Mapped = StmtExpr; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_expr(self) - } -} -pub fn fold_stmt_expr + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtExpr, -) -> Result, F::Error> { - let StmtExpr { value, range } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(StmtExpr { value, range }) -} -impl Foldable for StmtPass { - type Mapped = StmtPass; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_pass(self) - } -} -pub fn fold_stmt_pass + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtPass, -) -> Result, F::Error> { - let StmtPass { range } = node; - let context = folder.will_map_user(&range); - - let range = folder.map_user(range, context)?; - Ok(StmtPass { range }) -} -impl Foldable for StmtBreak { - type Mapped = StmtBreak; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_break(self) - } -} -pub fn fold_stmt_break + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtBreak, -) -> Result, F::Error> { - let StmtBreak { range } = node; - let context = folder.will_map_user(&range); - - let range = folder.map_user(range, context)?; - Ok(StmtBreak { range }) -} -impl Foldable for StmtContinue { - type Mapped = StmtContinue; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_stmt_continue(self) - } -} -pub fn fold_stmt_continue + ?Sized>( - #[allow(unused)] folder: &mut F, - node: StmtContinue, -) -> Result, F::Error> { - let StmtContinue { range } = node; - let context = folder.will_map_user(&range); - - let range = folder.map_user(range, context)?; - Ok(StmtContinue { range }) -} -impl Foldable for Expr { - type Mapped = Expr; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr(self) - } -} -pub fn fold_expr + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Expr, -) -> Result, F::Error> { - let folded = match node { - Expr::BoolOp(cons) => Expr::BoolOp(Foldable::fold(cons, folder)?), - Expr::NamedExpr(cons) => Expr::NamedExpr(Foldable::fold(cons, folder)?), - Expr::BinOp(cons) => Expr::BinOp(Foldable::fold(cons, folder)?), - Expr::UnaryOp(cons) => Expr::UnaryOp(Foldable::fold(cons, folder)?), - Expr::Lambda(cons) => Expr::Lambda(Foldable::fold(cons, folder)?), - Expr::IfExp(cons) => Expr::IfExp(Foldable::fold(cons, folder)?), - Expr::Dict(cons) => Expr::Dict(Foldable::fold(cons, folder)?), - Expr::Set(cons) => Expr::Set(Foldable::fold(cons, folder)?), - Expr::ListComp(cons) => Expr::ListComp(Foldable::fold(cons, folder)?), - Expr::SetComp(cons) => Expr::SetComp(Foldable::fold(cons, folder)?), - Expr::DictComp(cons) => Expr::DictComp(Foldable::fold(cons, folder)?), - Expr::GeneratorExp(cons) => Expr::GeneratorExp(Foldable::fold(cons, folder)?), - Expr::Await(cons) => Expr::Await(Foldable::fold(cons, folder)?), - Expr::Yield(cons) => Expr::Yield(Foldable::fold(cons, folder)?), - Expr::YieldFrom(cons) => Expr::YieldFrom(Foldable::fold(cons, folder)?), - Expr::Compare(cons) => Expr::Compare(Foldable::fold(cons, folder)?), - Expr::Call(cons) => Expr::Call(Foldable::fold(cons, folder)?), - Expr::FormattedValue(cons) => Expr::FormattedValue(Foldable::fold(cons, folder)?), - Expr::JoinedStr(cons) => Expr::JoinedStr(Foldable::fold(cons, folder)?), - Expr::Constant(cons) => Expr::Constant(Foldable::fold(cons, folder)?), - Expr::Attribute(cons) => Expr::Attribute(Foldable::fold(cons, folder)?), - Expr::Subscript(cons) => Expr::Subscript(Foldable::fold(cons, folder)?), - Expr::Starred(cons) => Expr::Starred(Foldable::fold(cons, folder)?), - Expr::Name(cons) => Expr::Name(Foldable::fold(cons, folder)?), - Expr::List(cons) => Expr::List(Foldable::fold(cons, folder)?), - Expr::Tuple(cons) => Expr::Tuple(Foldable::fold(cons, folder)?), - Expr::Slice(cons) => Expr::Slice(Foldable::fold(cons, folder)?), - }; - Ok(folded) -} -impl Foldable for ExprBoolOp { - type Mapped = ExprBoolOp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_bool_op(self) - } -} -pub fn fold_expr_bool_op + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprBoolOp, -) -> Result, F::Error> { - let ExprBoolOp { op, values, range } = node; - let context = folder.will_map_user(&range); - - let op = Foldable::fold(op, folder)?; - let values = Foldable::fold(values, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprBoolOp { op, values, range }) -} -impl Foldable for ExprNamedExpr { - type Mapped = ExprNamedExpr; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_named_expr(self) - } -} -pub fn fold_expr_named_expr + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprNamedExpr, -) -> Result, F::Error> { - let ExprNamedExpr { - target, - value, - range, - } = node; - let context = folder.will_map_user(&range); - - let target = Foldable::fold(target, folder)?; - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprNamedExpr { - target, - value, - range, - }) -} -impl Foldable for ExprBinOp { - type Mapped = ExprBinOp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_bin_op(self) - } -} -pub fn fold_expr_bin_op + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprBinOp, -) -> Result, F::Error> { - let ExprBinOp { - left, - op, - right, - range, - } = node; - let context = folder.will_map_user(&range); - - let left = Foldable::fold(left, folder)?; - let op = Foldable::fold(op, folder)?; - let right = Foldable::fold(right, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprBinOp { - left, - op, - right, - range, - }) -} -impl Foldable for ExprUnaryOp { - type Mapped = ExprUnaryOp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_unary_op(self) - } -} -pub fn fold_expr_unary_op + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprUnaryOp, -) -> Result, F::Error> { - let ExprUnaryOp { op, operand, range } = node; - let context = folder.will_map_user(&range); - - let op = Foldable::fold(op, folder)?; - let operand = Foldable::fold(operand, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprUnaryOp { op, operand, range }) -} -impl Foldable for ExprLambda { - type Mapped = ExprLambda; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_lambda(self) - } -} -pub fn fold_expr_lambda + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprLambda, -) -> Result, F::Error> { - let ExprLambda { args, body, range } = node; - let context = folder.will_map_user(&range); - - let args = Foldable::fold(args, folder)?; - let body = Foldable::fold(body, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprLambda { args, body, range }) -} -impl Foldable for ExprIfExp { - type Mapped = ExprIfExp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_if_exp(self) - } -} -pub fn fold_expr_if_exp + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprIfExp, -) -> Result, F::Error> { - let ExprIfExp { - test, - body, - orelse, - range, - } = node; - let context = folder.will_map_user(&range); - - let test = Foldable::fold(test, folder)?; - let body = Foldable::fold(body, folder)?; - let orelse = Foldable::fold(orelse, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprIfExp { - test, - body, - orelse, - range, - }) -} -impl Foldable for ExprDict { - type Mapped = ExprDict; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_dict(self) - } -} -pub fn fold_expr_dict + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprDict, -) -> Result, F::Error> { - let ExprDict { - keys, - values, - range, - } = node; - let context = folder.will_map_user(&range); - - let keys = Foldable::fold(keys, folder)?; - let values = Foldable::fold(values, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprDict { - keys, - values, - range, - }) -} -impl Foldable for ExprSet { - type Mapped = ExprSet; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_set(self) - } -} -pub fn fold_expr_set + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprSet, -) -> Result, F::Error> { - let ExprSet { elts, range } = node; - let context = folder.will_map_user(&range); - - let elts = Foldable::fold(elts, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprSet { elts, range }) -} -impl Foldable for ExprListComp { - type Mapped = ExprListComp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_list_comp(self) - } -} -pub fn fold_expr_list_comp + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprListComp, -) -> Result, F::Error> { - let ExprListComp { - elt, - generators, - range, - } = node; - let context = folder.will_map_user(&range); - - let elt = Foldable::fold(elt, folder)?; - let generators = Foldable::fold(generators, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprListComp { - elt, - generators, - range, - }) -} -impl Foldable for ExprSetComp { - type Mapped = ExprSetComp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_set_comp(self) - } -} -pub fn fold_expr_set_comp + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprSetComp, -) -> Result, F::Error> { - let ExprSetComp { - elt, - generators, - range, - } = node; - let context = folder.will_map_user(&range); - - let elt = Foldable::fold(elt, folder)?; - let generators = Foldable::fold(generators, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprSetComp { - elt, - generators, - range, - }) -} -impl Foldable for ExprDictComp { - type Mapped = ExprDictComp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_dict_comp(self) - } -} -pub fn fold_expr_dict_comp + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprDictComp, -) -> Result, F::Error> { - let ExprDictComp { - key, - value, - generators, - range, - } = node; - let context = folder.will_map_user(&range); - - let key = Foldable::fold(key, folder)?; - let value = Foldable::fold(value, folder)?; - let generators = Foldable::fold(generators, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprDictComp { - key, - value, - generators, - range, - }) -} -impl Foldable for ExprGeneratorExp { - type Mapped = ExprGeneratorExp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_generator_exp(self) - } -} -pub fn fold_expr_generator_exp + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprGeneratorExp, -) -> Result, F::Error> { - let ExprGeneratorExp { - elt, - generators, - range, - } = node; - let context = folder.will_map_user(&range); - - let elt = Foldable::fold(elt, folder)?; - let generators = Foldable::fold(generators, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprGeneratorExp { - elt, - generators, - range, - }) -} -impl Foldable for ExprAwait { - type Mapped = ExprAwait; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_await(self) - } -} -pub fn fold_expr_await + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprAwait, -) -> Result, F::Error> { - let ExprAwait { value, range } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprAwait { value, range }) -} -impl Foldable for ExprYield { - type Mapped = ExprYield; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_yield(self) - } -} -pub fn fold_expr_yield + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprYield, -) -> Result, F::Error> { - let ExprYield { value, range } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprYield { value, range }) -} -impl Foldable for ExprYieldFrom { - type Mapped = ExprYieldFrom; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_yield_from(self) - } -} -pub fn fold_expr_yield_from + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprYieldFrom, -) -> Result, F::Error> { - let ExprYieldFrom { value, range } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprYieldFrom { value, range }) -} -impl Foldable for ExprCompare { - type Mapped = ExprCompare; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_compare(self) - } -} -pub fn fold_expr_compare + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprCompare, -) -> Result, F::Error> { - let ExprCompare { - left, - ops, - comparators, - range, - } = node; - let context = folder.will_map_user(&range); - - let left = Foldable::fold(left, folder)?; - let ops = Foldable::fold(ops, folder)?; - let comparators = Foldable::fold(comparators, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprCompare { - left, - ops, - comparators, - range, - }) -} -impl Foldable for ExprCall { - type Mapped = ExprCall; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_call(self) - } -} -pub fn fold_expr_call + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprCall, -) -> Result, F::Error> { - let ExprCall { - func, - args, - keywords, - range, - } = node; - let context = folder.will_map_user(&range); - - let func = Foldable::fold(func, folder)?; - let args = Foldable::fold(args, folder)?; - let keywords = Foldable::fold(keywords, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprCall { - func, - args, - keywords, - range, - }) -} -impl Foldable for ExprFormattedValue { - type Mapped = ExprFormattedValue; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_formatted_value(self) - } -} -pub fn fold_expr_formatted_value + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprFormattedValue, -) -> Result, F::Error> { - let ExprFormattedValue { - value, - conversion, - format_spec, - range, - } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let conversion = Foldable::fold(conversion, folder)?; - let format_spec = Foldable::fold(format_spec, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprFormattedValue { - value, - conversion, - format_spec, - range, - }) -} -impl Foldable for ExprJoinedStr { - type Mapped = ExprJoinedStr; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_joined_str(self) - } -} -pub fn fold_expr_joined_str + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprJoinedStr, -) -> Result, F::Error> { - let ExprJoinedStr { values, range } = node; - let context = folder.will_map_user(&range); - - let values = Foldable::fold(values, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprJoinedStr { values, range }) -} -impl Foldable for ExprConstant { - type Mapped = ExprConstant; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_constant(self) - } -} -pub fn fold_expr_constant + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprConstant, -) -> Result, F::Error> { - let ExprConstant { value, kind, range } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let kind = Foldable::fold(kind, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprConstant { value, kind, range }) -} -impl Foldable for ExprAttribute { - type Mapped = ExprAttribute; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_attribute(self) - } -} -pub fn fold_expr_attribute + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprAttribute, -) -> Result, F::Error> { - let ExprAttribute { - value, - attr, - ctx, - range, - } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let attr = Foldable::fold(attr, folder)?; - let ctx = Foldable::fold(ctx, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprAttribute { - value, - attr, - ctx, - range, - }) -} -impl Foldable for ExprSubscript { - type Mapped = ExprSubscript; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_subscript(self) - } -} -pub fn fold_expr_subscript + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprSubscript, -) -> Result, F::Error> { - let ExprSubscript { - value, - slice, - ctx, - range, - } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let slice = Foldable::fold(slice, folder)?; - let ctx = Foldable::fold(ctx, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprSubscript { - value, - slice, - ctx, - range, - }) -} -impl Foldable for ExprStarred { - type Mapped = ExprStarred; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_starred(self) - } -} -pub fn fold_expr_starred + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprStarred, -) -> Result, F::Error> { - let ExprStarred { value, ctx, range } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let ctx = Foldable::fold(ctx, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprStarred { value, ctx, range }) -} -impl Foldable for ExprName { - type Mapped = ExprName; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_name(self) - } -} -pub fn fold_expr_name + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprName, -) -> Result, F::Error> { - let ExprName { id, ctx, range } = node; - let context = folder.will_map_user(&range); - - let id = Foldable::fold(id, folder)?; - let ctx = Foldable::fold(ctx, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprName { id, ctx, range }) -} -impl Foldable for ExprList { - type Mapped = ExprList; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_list(self) - } -} -pub fn fold_expr_list + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprList, -) -> Result, F::Error> { - let ExprList { elts, ctx, range } = node; - let context = folder.will_map_user(&range); - - let elts = Foldable::fold(elts, folder)?; - let ctx = Foldable::fold(ctx, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprList { elts, ctx, range }) -} -impl Foldable for ExprTuple { - type Mapped = ExprTuple; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_tuple(self) - } -} -pub fn fold_expr_tuple + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprTuple, -) -> Result, F::Error> { - let ExprTuple { elts, ctx, range } = node; - let context = folder.will_map_user(&range); - - let elts = Foldable::fold(elts, folder)?; - let ctx = Foldable::fold(ctx, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprTuple { elts, ctx, range }) -} -impl Foldable for ExprSlice { - type Mapped = ExprSlice; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_slice(self) - } -} -pub fn fold_expr_slice + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprSlice, -) -> Result, F::Error> { - let ExprSlice { - lower, - upper, - step, - range, - } = node; - let context = folder.will_map_user(&range); - - let lower = Foldable::fold(lower, folder)?; - let upper = Foldable::fold(upper, folder)?; - let step = Foldable::fold(step, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExprSlice { - lower, - upper, - step, - range, - }) -} -impl Foldable for ExprContext { - type Mapped = ExprContext; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_expr_context(self) - } -} -pub fn fold_expr_context + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExprContext, -) -> Result { - Ok(node) -} -impl Foldable for BoolOp { - type Mapped = BoolOp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_boolop(self) - } -} -pub fn fold_boolop + ?Sized>( - #[allow(unused)] folder: &mut F, - node: BoolOp, -) -> Result { - Ok(node) -} -impl Foldable for Operator { - type Mapped = Operator; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_operator(self) - } -} -pub fn fold_operator + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Operator, -) -> Result { - Ok(node) -} -impl Foldable for UnaryOp { - type Mapped = UnaryOp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_unaryop(self) - } -} -pub fn fold_unaryop + ?Sized>( - #[allow(unused)] folder: &mut F, - node: UnaryOp, -) -> Result { - Ok(node) -} -impl Foldable for CmpOp { - type Mapped = CmpOp; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_cmpop(self) - } -} -pub fn fold_cmpop + ?Sized>( - #[allow(unused)] folder: &mut F, - node: CmpOp, -) -> Result { - Ok(node) -} -impl Foldable for Comprehension { - type Mapped = Comprehension; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_comprehension(self) - } -} -pub fn fold_comprehension + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Comprehension, -) -> Result, F::Error> { - let Comprehension { - target, - iter, - ifs, - is_async, - range, - } = node; - let context = folder.will_map_user_cfg(&range); - let target = Foldable::fold(target, folder)?; - let iter = Foldable::fold(iter, folder)?; - let ifs = Foldable::fold(ifs, folder)?; - let is_async = Foldable::fold(is_async, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(Comprehension { - target, - iter, - ifs, - is_async, - range, - }) -} -impl Foldable for ExceptHandler { - type Mapped = ExceptHandler; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_excepthandler(self) - } -} -pub fn fold_excepthandler + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExceptHandler, -) -> Result, F::Error> { - let folded = match node { - ExceptHandler::ExceptHandler(cons) => { - ExceptHandler::ExceptHandler(Foldable::fold(cons, folder)?) - } - }; - Ok(folded) -} -impl Foldable for ExceptHandlerExceptHandler { - type Mapped = ExceptHandlerExceptHandler; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_excepthandler_except_handler(self) - } -} -pub fn fold_excepthandler_except_handler + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ExceptHandlerExceptHandler, -) -> Result, F::Error> { - let ExceptHandlerExceptHandler { - type_, - name, - body, - range, - } = node; - let context = folder.will_map_user(&range); - - let type_ = Foldable::fold(type_, folder)?; - let name = Foldable::fold(name, folder)?; - let body = Foldable::fold(body, folder)?; - let range = folder.map_user(range, context)?; - Ok(ExceptHandlerExceptHandler { - type_, - name, - body, - range, - }) -} -impl Foldable for Arguments { - type Mapped = Arguments; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_arguments(self) - } -} -pub fn fold_arguments + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Arguments, -) -> Result, F::Error> { - let Arguments { - posonlyargs, - args, - vararg, - kwonlyargs, - kwarg, - range, - } = node; - let context = folder.will_map_user_cfg(&range); - let posonlyargs = Foldable::fold(posonlyargs, folder)?; - let args = Foldable::fold(args, folder)?; - let vararg = Foldable::fold(vararg, folder)?; - let kwonlyargs = Foldable::fold(kwonlyargs, folder)?; - let kwarg = Foldable::fold(kwarg, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(Arguments { - posonlyargs, - args, - vararg, - kwonlyargs, - kwarg, - range, - }) -} -impl Foldable for Arg { - type Mapped = Arg; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_arg(self) - } -} -pub fn fold_arg + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Arg, -) -> Result, F::Error> { - let Arg { - arg, - annotation, - type_comment, - range, - } = node; - let context = folder.will_map_user(&range); - let arg = Foldable::fold(arg, folder)?; - let annotation = Foldable::fold(annotation, folder)?; - let type_comment = Foldable::fold(type_comment, folder)?; - let range = folder.map_user(range, context)?; - Ok(Arg { - arg, - annotation, - type_comment, - range, - }) -} -impl Foldable for Keyword { - type Mapped = Keyword; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_keyword(self) - } -} -pub fn fold_keyword + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Keyword, -) -> Result, F::Error> { - let Keyword { arg, value, range } = node; - let context = folder.will_map_user(&range); - let arg = Foldable::fold(arg, folder)?; - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(Keyword { arg, value, range }) -} -impl Foldable for Alias { - type Mapped = Alias; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_alias(self) - } -} -pub fn fold_alias + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Alias, -) -> Result, F::Error> { - let Alias { - name, - asname, - range, - } = node; - let context = folder.will_map_user(&range); - let name = Foldable::fold(name, folder)?; - let asname = Foldable::fold(asname, folder)?; - let range = folder.map_user(range, context)?; - Ok(Alias { - name, - asname, - range, - }) -} -impl Foldable for WithItem { - type Mapped = WithItem; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_withitem(self) - } -} -pub fn fold_withitem + ?Sized>( - #[allow(unused)] folder: &mut F, - node: WithItem, -) -> Result, F::Error> { - let WithItem { - context_expr, - optional_vars, - range, - } = node; - let context = folder.will_map_user_cfg(&range); - let context_expr = Foldable::fold(context_expr, folder)?; - let optional_vars = Foldable::fold(optional_vars, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(WithItem { - context_expr, - optional_vars, - range, - }) -} -impl Foldable for MatchCase { - type Mapped = MatchCase; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_match_case(self) - } -} -pub fn fold_match_case + ?Sized>( - #[allow(unused)] folder: &mut F, - node: MatchCase, -) -> Result, F::Error> { - let MatchCase { - pattern, - guard, - body, - range, - } = node; - let context = folder.will_map_user_cfg(&range); - let pattern = Foldable::fold(pattern, folder)?; - let guard = Foldable::fold(guard, folder)?; - let body = Foldable::fold(body, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(MatchCase { - pattern, - guard, - body, - range, - }) -} -impl Foldable for Pattern { - type Mapped = Pattern; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_pattern(self) - } -} -pub fn fold_pattern + ?Sized>( - #[allow(unused)] folder: &mut F, - node: Pattern, -) -> Result, F::Error> { - let folded = match node { - Pattern::MatchValue(cons) => Pattern::MatchValue(Foldable::fold(cons, folder)?), - Pattern::MatchSingleton(cons) => Pattern::MatchSingleton(Foldable::fold(cons, folder)?), - Pattern::MatchSequence(cons) => Pattern::MatchSequence(Foldable::fold(cons, folder)?), - Pattern::MatchMapping(cons) => Pattern::MatchMapping(Foldable::fold(cons, folder)?), - Pattern::MatchClass(cons) => Pattern::MatchClass(Foldable::fold(cons, folder)?), - Pattern::MatchStar(cons) => Pattern::MatchStar(Foldable::fold(cons, folder)?), - Pattern::MatchAs(cons) => Pattern::MatchAs(Foldable::fold(cons, folder)?), - Pattern::MatchOr(cons) => Pattern::MatchOr(Foldable::fold(cons, folder)?), - }; - Ok(folded) -} -impl Foldable for PatternMatchValue { - type Mapped = PatternMatchValue; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_pattern_match_value(self) - } -} -pub fn fold_pattern_match_value + ?Sized>( - #[allow(unused)] folder: &mut F, - node: PatternMatchValue, -) -> Result, F::Error> { - let PatternMatchValue { value, range } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(PatternMatchValue { value, range }) -} -impl Foldable for PatternMatchSingleton { - type Mapped = PatternMatchSingleton; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_pattern_match_singleton(self) - } -} -pub fn fold_pattern_match_singleton + ?Sized>( - #[allow(unused)] folder: &mut F, - node: PatternMatchSingleton, -) -> Result, F::Error> { - let PatternMatchSingleton { value, range } = node; - let context = folder.will_map_user(&range); - - let value = Foldable::fold(value, folder)?; - let range = folder.map_user(range, context)?; - Ok(PatternMatchSingleton { value, range }) -} -impl Foldable for PatternMatchSequence { - type Mapped = PatternMatchSequence; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_pattern_match_sequence(self) - } -} -pub fn fold_pattern_match_sequence + ?Sized>( - #[allow(unused)] folder: &mut F, - node: PatternMatchSequence, -) -> Result, F::Error> { - let PatternMatchSequence { patterns, range } = node; - let context = folder.will_map_user(&range); - - let patterns = Foldable::fold(patterns, folder)?; - let range = folder.map_user(range, context)?; - Ok(PatternMatchSequence { patterns, range }) -} -impl Foldable for PatternMatchMapping { - type Mapped = PatternMatchMapping; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_pattern_match_mapping(self) - } -} -pub fn fold_pattern_match_mapping + ?Sized>( - #[allow(unused)] folder: &mut F, - node: PatternMatchMapping, -) -> Result, F::Error> { - let PatternMatchMapping { - keys, - patterns, - rest, - range, - } = node; - let context = folder.will_map_user(&range); - - let keys = Foldable::fold(keys, folder)?; - let patterns = Foldable::fold(patterns, folder)?; - let rest = Foldable::fold(rest, folder)?; - let range = folder.map_user(range, context)?; - Ok(PatternMatchMapping { - keys, - patterns, - rest, - range, - }) -} -impl Foldable for PatternMatchClass { - type Mapped = PatternMatchClass; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_pattern_match_class(self) - } -} -pub fn fold_pattern_match_class + ?Sized>( - #[allow(unused)] folder: &mut F, - node: PatternMatchClass, -) -> Result, F::Error> { - let PatternMatchClass { - cls, - patterns, - kwd_attrs, - kwd_patterns, - range, - } = node; - let context = folder.will_map_user(&range); - - let cls = Foldable::fold(cls, folder)?; - let patterns = Foldable::fold(patterns, folder)?; - let kwd_attrs = Foldable::fold(kwd_attrs, folder)?; - let kwd_patterns = Foldable::fold(kwd_patterns, folder)?; - let range = folder.map_user(range, context)?; - Ok(PatternMatchClass { - cls, - patterns, - kwd_attrs, - kwd_patterns, - range, - }) -} -impl Foldable for PatternMatchStar { - type Mapped = PatternMatchStar; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_pattern_match_star(self) - } -} -pub fn fold_pattern_match_star + ?Sized>( - #[allow(unused)] folder: &mut F, - node: PatternMatchStar, -) -> Result, F::Error> { - let PatternMatchStar { name, range } = node; - let context = folder.will_map_user(&range); - - let name = Foldable::fold(name, folder)?; - let range = folder.map_user(range, context)?; - Ok(PatternMatchStar { name, range }) -} -impl Foldable for PatternMatchAs { - type Mapped = PatternMatchAs; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_pattern_match_as(self) - } -} -pub fn fold_pattern_match_as + ?Sized>( - #[allow(unused)] folder: &mut F, - node: PatternMatchAs, -) -> Result, F::Error> { - let PatternMatchAs { - pattern, - name, - range, - } = node; - let context = folder.will_map_user(&range); - - let pattern = Foldable::fold(pattern, folder)?; - let name = Foldable::fold(name, folder)?; - let range = folder.map_user(range, context)?; - Ok(PatternMatchAs { - pattern, - name, - range, - }) -} -impl Foldable for PatternMatchOr { - type Mapped = PatternMatchOr; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_pattern_match_or(self) - } -} -pub fn fold_pattern_match_or + ?Sized>( - #[allow(unused)] folder: &mut F, - node: PatternMatchOr, -) -> Result, F::Error> { - let PatternMatchOr { patterns, range } = node; - let context = folder.will_map_user(&range); - - let patterns = Foldable::fold(patterns, folder)?; - let range = folder.map_user(range, context)?; - Ok(PatternMatchOr { patterns, range }) -} -impl Foldable for TypeIgnore { - type Mapped = TypeIgnore; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_type_ignore(self) - } -} -pub fn fold_type_ignore + ?Sized>( - #[allow(unused)] folder: &mut F, - node: TypeIgnore, -) -> Result, F::Error> { - let folded = match node { - TypeIgnore::TypeIgnore(cons) => TypeIgnore::TypeIgnore(Foldable::fold(cons, folder)?), - }; - Ok(folded) -} -impl Foldable for TypeIgnoreTypeIgnore { - type Mapped = TypeIgnoreTypeIgnore; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_type_ignore_type_ignore(self) - } -} -pub fn fold_type_ignore_type_ignore + ?Sized>( - #[allow(unused)] folder: &mut F, - node: TypeIgnoreTypeIgnore, -) -> Result, F::Error> { - let TypeIgnoreTypeIgnore { lineno, tag, range } = node; - let context = folder.will_map_user_cfg(&range); - - let lineno = Foldable::fold(lineno, folder)?; - let tag = Foldable::fold(tag, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(TypeIgnoreTypeIgnore { lineno, tag, range }) -} -impl Foldable for TypeParam { - type Mapped = TypeParam; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_type_param(self) - } -} -pub fn fold_type_param + ?Sized>( - #[allow(unused)] folder: &mut F, - node: TypeParam, -) -> Result, F::Error> { - let folded = match node { - TypeParam::TypeVar(cons) => TypeParam::TypeVar(Foldable::fold(cons, folder)?), - TypeParam::ParamSpec(cons) => TypeParam::ParamSpec(Foldable::fold(cons, folder)?), - TypeParam::TypeVarTuple(cons) => TypeParam::TypeVarTuple(Foldable::fold(cons, folder)?), - }; - Ok(folded) -} -impl Foldable for TypeParamTypeVar { - type Mapped = TypeParamTypeVar; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_type_param_type_var(self) - } -} -pub fn fold_type_param_type_var + ?Sized>( - #[allow(unused)] folder: &mut F, - node: TypeParamTypeVar, -) -> Result, F::Error> { - let TypeParamTypeVar { name, bound, range } = node; - let context = folder.will_map_user(&range); - - let name = Foldable::fold(name, folder)?; - let bound = Foldable::fold(bound, folder)?; - let range = folder.map_user(range, context)?; - Ok(TypeParamTypeVar { name, bound, range }) -} -impl Foldable for TypeParamParamSpec { - type Mapped = TypeParamParamSpec; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_type_param_param_spec(self) - } -} -pub fn fold_type_param_param_spec + ?Sized>( - #[allow(unused)] folder: &mut F, - node: TypeParamParamSpec, -) -> Result, F::Error> { - let TypeParamParamSpec { name, range } = node; - let context = folder.will_map_user(&range); - - let name = Foldable::fold(name, folder)?; - let range = folder.map_user(range, context)?; - Ok(TypeParamParamSpec { name, range }) -} -impl Foldable for TypeParamTypeVarTuple { - type Mapped = TypeParamTypeVarTuple; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_type_param_type_var_tuple(self) - } -} -pub fn fold_type_param_type_var_tuple + ?Sized>( - #[allow(unused)] folder: &mut F, - node: TypeParamTypeVarTuple, -) -> Result, F::Error> { - let TypeParamTypeVarTuple { name, range } = node; - let context = folder.will_map_user(&range); - - let name = Foldable::fold(name, folder)?; - let range = folder.map_user(range, context)?; - Ok(TypeParamTypeVarTuple { name, range }) -} -impl Foldable for ArgWithDefault { - type Mapped = ArgWithDefault; - fn fold + ?Sized>( - self, - folder: &mut F, - ) -> Result { - folder.fold_arg_with_default(self) - } -} -pub fn fold_arg_with_default + ?Sized>( - #[allow(unused)] folder: &mut F, - node: ArgWithDefault, -) -> Result, F::Error> { - let ArgWithDefault { - def, - default, - range, - } = node; - let context = folder.will_map_user_cfg(&range); - let def = Foldable::fold(def, folder)?; - let default = Foldable::fold(default, folder)?; - let range = folder.map_user_cfg(range, context)?; - Ok(ArgWithDefault { - def, - default, - range, - }) -} diff --git a/ast/src/gen/generic.rs b/ast/src/gen/generic.rs deleted file mode 100644 index 5efe9c05..00000000 --- a/ast/src/gen/generic.rs +++ /dev/null @@ -1,3241 +0,0 @@ -// File automatically generated by ast/asdl_rs.py. - -use crate::text_size::TextRange; -#[derive(Clone, Debug, PartialEq, is_macro::Is)] -pub enum Ast { - #[is(name = "module")] - Mod(Mod), - Stmt(Stmt), - Expr(Expr), - ExprContext(ExprContext), - BoolOp(BoolOp), - Operator(Operator), - UnaryOp(UnaryOp), - CmpOp(CmpOp), - Comprehension(Comprehension), - ExceptHandler(ExceptHandler), - Arguments(Arguments), - Arg(Arg), - Keyword(Keyword), - Alias(Alias), - WithItem(WithItem), - MatchCase(MatchCase), - Pattern(Pattern), - TypeIgnore(TypeIgnore), - TypeParam(TypeParam), -} -impl Node for Ast { - const NAME: &'static str = "AST"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -impl From> for Ast { - fn from(node: Mod) -> Self { - Ast::Mod(node) - } -} - -impl From> for Ast { - fn from(node: Stmt) -> Self { - Ast::Stmt(node) - } -} - -impl From> for Ast { - fn from(node: Expr) -> Self { - Ast::Expr(node) - } -} - -impl From for Ast { - fn from(node: ExprContext) -> Self { - Ast::ExprContext(node) - } -} - -impl From for Ast { - fn from(node: BoolOp) -> Self { - Ast::BoolOp(node) - } -} - -impl From for Ast { - fn from(node: Operator) -> Self { - Ast::Operator(node) - } -} - -impl From for Ast { - fn from(node: UnaryOp) -> Self { - Ast::UnaryOp(node) - } -} - -impl From for Ast { - fn from(node: CmpOp) -> Self { - Ast::CmpOp(node) - } -} - -impl From> for Ast { - fn from(node: Comprehension) -> Self { - Ast::Comprehension(node) - } -} - -impl From> for Ast { - fn from(node: ExceptHandler) -> Self { - Ast::ExceptHandler(node) - } -} - -impl From> for Ast { - fn from(node: Arguments) -> Self { - Ast::Arguments(node) - } -} - -impl From> for Ast { - fn from(node: Arg) -> Self { - Ast::Arg(node) - } -} - -impl From> for Ast { - fn from(node: Keyword) -> Self { - Ast::Keyword(node) - } -} - -impl From> for Ast { - fn from(node: Alias) -> Self { - Ast::Alias(node) - } -} - -impl From> for Ast { - fn from(node: WithItem) -> Self { - Ast::WithItem(node) - } -} - -impl From> for Ast { - fn from(node: MatchCase) -> Self { - Ast::MatchCase(node) - } -} - -impl From> for Ast { - fn from(node: Pattern) -> Self { - Ast::Pattern(node) - } -} - -impl From> for Ast { - fn from(node: TypeIgnore) -> Self { - Ast::TypeIgnore(node) - } -} - -impl From> for Ast { - fn from(node: TypeParam) -> Self { - Ast::TypeParam(node) - } -} - -/// See also [mod](https://docs.python.org/3/library/ast.html#ast.mod) -#[derive(Clone, Debug, PartialEq, is_macro::Is)] -pub enum Mod { - Module(ModModule), - Interactive(ModInteractive), - Expression(ModExpression), - FunctionType(ModFunctionType), -} - -/// See also [Module](https://docs.python.org/3/library/ast.html#ast.Module) -#[derive(Clone, Debug, PartialEq)] -pub struct ModModule { - pub range: OptionalRange, - pub body: Vec>, - pub type_ignores: Vec>, -} - -impl Node for ModModule { - const NAME: &'static str = "Module"; - const FIELD_NAMES: &'static [&'static str] = &["body", "type_ignores"]; -} -impl From> for Mod { - fn from(payload: ModModule) -> Self { - Mod::Module(payload) - } -} -impl From> for Ast { - fn from(payload: ModModule) -> Self { - Mod::from(payload).into() - } -} - -/// See also [Interactive](https://docs.python.org/3/library/ast.html#ast.Interactive) -#[derive(Clone, Debug, PartialEq)] -pub struct ModInteractive { - pub range: OptionalRange, - pub body: Vec>, -} - -impl Node for ModInteractive { - const NAME: &'static str = "Interactive"; - const FIELD_NAMES: &'static [&'static str] = &["body"]; -} -impl From> for Mod { - fn from(payload: ModInteractive) -> Self { - Mod::Interactive(payload) - } -} -impl From> for Ast { - fn from(payload: ModInteractive) -> Self { - Mod::from(payload).into() - } -} - -/// See also [Expression](https://docs.python.org/3/library/ast.html#ast.Expression) -#[derive(Clone, Debug, PartialEq)] -pub struct ModExpression { - pub range: OptionalRange, - pub body: Box>, -} - -impl Node for ModExpression { - const NAME: &'static str = "Expression"; - const FIELD_NAMES: &'static [&'static str] = &["body"]; -} -impl From> for Mod { - fn from(payload: ModExpression) -> Self { - Mod::Expression(payload) - } -} -impl From> for Ast { - fn from(payload: ModExpression) -> Self { - Mod::from(payload).into() - } -} - -/// See also [FunctionType](https://docs.python.org/3/library/ast.html#ast.FunctionType) -#[derive(Clone, Debug, PartialEq)] -pub struct ModFunctionType { - pub range: OptionalRange, - pub argtypes: Vec>, - pub returns: Box>, -} - -impl Node for ModFunctionType { - const NAME: &'static str = "FunctionType"; - const FIELD_NAMES: &'static [&'static str] = &["argtypes", "returns"]; -} -impl From> for Mod { - fn from(payload: ModFunctionType) -> Self { - Mod::FunctionType(payload) - } -} -impl From> for Ast { - fn from(payload: ModFunctionType) -> Self { - Mod::from(payload).into() - } -} - -impl Node for Mod { - const NAME: &'static str = "mod"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [stmt](https://docs.python.org/3/library/ast.html#ast.stmt) -#[derive(Clone, Debug, PartialEq, is_macro::Is)] -pub enum Stmt { - #[is(name = "function_def_stmt")] - FunctionDef(StmtFunctionDef), - #[is(name = "async_function_def_stmt")] - AsyncFunctionDef(StmtAsyncFunctionDef), - #[is(name = "class_def_stmt")] - ClassDef(StmtClassDef), - #[is(name = "return_stmt")] - Return(StmtReturn), - #[is(name = "delete_stmt")] - Delete(StmtDelete), - #[is(name = "assign_stmt")] - Assign(StmtAssign), - #[is(name = "type_alias_stmt")] - TypeAlias(StmtTypeAlias), - #[is(name = "aug_assign_stmt")] - AugAssign(StmtAugAssign), - #[is(name = "ann_assign_stmt")] - AnnAssign(StmtAnnAssign), - #[is(name = "for_stmt")] - For(StmtFor), - #[is(name = "async_for_stmt")] - AsyncFor(StmtAsyncFor), - #[is(name = "while_stmt")] - While(StmtWhile), - #[is(name = "if_stmt")] - If(StmtIf), - #[is(name = "with_stmt")] - With(StmtWith), - #[is(name = "async_with_stmt")] - AsyncWith(StmtAsyncWith), - #[is(name = "match_stmt")] - Match(StmtMatch), - #[is(name = "raise_stmt")] - Raise(StmtRaise), - #[is(name = "try_stmt")] - Try(StmtTry), - #[is(name = "try_star_stmt")] - TryStar(StmtTryStar), - #[is(name = "assert_stmt")] - Assert(StmtAssert), - #[is(name = "import_stmt")] - Import(StmtImport), - #[is(name = "import_from_stmt")] - ImportFrom(StmtImportFrom), - #[is(name = "global_stmt")] - Global(StmtGlobal), - #[is(name = "nonlocal_stmt")] - Nonlocal(StmtNonlocal), - #[is(name = "expr_stmt")] - Expr(StmtExpr), - #[is(name = "pass_stmt")] - Pass(StmtPass), - #[is(name = "break_stmt")] - Break(StmtBreak), - #[is(name = "continue_stmt")] - Continue(StmtContinue), -} - -/// See also [FunctionDef](https://docs.python.org/3/library/ast.html#ast.FunctionDef) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtFunctionDef { - pub range: R, - pub name: Identifier, - pub args: Box>, - pub body: Vec>, - pub decorator_list: Vec>, - pub returns: Option>>, - pub type_comment: Option, - pub type_params: Vec>, -} - -impl Node for StmtFunctionDef { - const NAME: &'static str = "FunctionDef"; - const FIELD_NAMES: &'static [&'static str] = &[ - "name", - "args", - "body", - "decorator_list", - "returns", - "type_comment", - "type_params", - ]; -} -impl From> for Stmt { - fn from(payload: StmtFunctionDef) -> Self { - Stmt::FunctionDef(payload) - } -} -impl From> for Ast { - fn from(payload: StmtFunctionDef) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [AsyncFunctionDef](https://docs.python.org/3/library/ast.html#ast.AsyncFunctionDef) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAsyncFunctionDef { - pub range: R, - pub name: Identifier, - pub args: Box>, - pub body: Vec>, - pub decorator_list: Vec>, - pub returns: Option>>, - pub type_comment: Option, - pub type_params: Vec>, -} - -impl Node for StmtAsyncFunctionDef { - const NAME: &'static str = "AsyncFunctionDef"; - const FIELD_NAMES: &'static [&'static str] = &[ - "name", - "args", - "body", - "decorator_list", - "returns", - "type_comment", - "type_params", - ]; -} -impl From> for Stmt { - fn from(payload: StmtAsyncFunctionDef) -> Self { - Stmt::AsyncFunctionDef(payload) - } -} -impl From> for Ast { - fn from(payload: StmtAsyncFunctionDef) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [ClassDef](https://docs.python.org/3/library/ast.html#ast.ClassDef) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtClassDef { - pub range: R, - pub name: Identifier, - pub bases: Vec>, - pub keywords: Vec>, - pub body: Vec>, - pub decorator_list: Vec>, - pub type_params: Vec>, -} - -impl Node for StmtClassDef { - const NAME: &'static str = "ClassDef"; - const FIELD_NAMES: &'static [&'static str] = &[ - "name", - "bases", - "keywords", - "body", - "decorator_list", - "type_params", - ]; -} -impl From> for Stmt { - fn from(payload: StmtClassDef) -> Self { - Stmt::ClassDef(payload) - } -} -impl From> for Ast { - fn from(payload: StmtClassDef) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Return](https://docs.python.org/3/library/ast.html#ast.Return) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtReturn { - pub range: R, - pub value: Option>>, -} - -impl Node for StmtReturn { - const NAME: &'static str = "Return"; - const FIELD_NAMES: &'static [&'static str] = &["value"]; -} -impl From> for Stmt { - fn from(payload: StmtReturn) -> Self { - Stmt::Return(payload) - } -} -impl From> for Ast { - fn from(payload: StmtReturn) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Delete](https://docs.python.org/3/library/ast.html#ast.Delete) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtDelete { - pub range: R, - pub targets: Vec>, -} - -impl Node for StmtDelete { - const NAME: &'static str = "Delete"; - const FIELD_NAMES: &'static [&'static str] = &["targets"]; -} -impl From> for Stmt { - fn from(payload: StmtDelete) -> Self { - Stmt::Delete(payload) - } -} -impl From> for Ast { - fn from(payload: StmtDelete) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Assign](https://docs.python.org/3/library/ast.html#ast.Assign) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAssign { - pub range: R, - pub targets: Vec>, - pub value: Box>, - pub type_comment: Option, -} - -impl Node for StmtAssign { - const NAME: &'static str = "Assign"; - const FIELD_NAMES: &'static [&'static str] = &["targets", "value", "type_comment"]; -} -impl From> for Stmt { - fn from(payload: StmtAssign) -> Self { - Stmt::Assign(payload) - } -} -impl From> for Ast { - fn from(payload: StmtAssign) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [TypeAlias](https://docs.python.org/3/library/ast.html#ast.TypeAlias) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtTypeAlias { - pub range: R, - pub name: Box>, - pub type_params: Vec>, - pub value: Box>, -} - -impl Node for StmtTypeAlias { - const NAME: &'static str = "TypeAlias"; - const FIELD_NAMES: &'static [&'static str] = &["name", "type_params", "value"]; -} -impl From> for Stmt { - fn from(payload: StmtTypeAlias) -> Self { - Stmt::TypeAlias(payload) - } -} -impl From> for Ast { - fn from(payload: StmtTypeAlias) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [AugAssign](https://docs.python.org/3/library/ast.html#ast.AugAssign) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAugAssign { - pub range: R, - pub target: Box>, - pub op: Operator, - pub value: Box>, -} - -impl Node for StmtAugAssign { - const NAME: &'static str = "AugAssign"; - const FIELD_NAMES: &'static [&'static str] = &["target", "op", "value"]; -} -impl From> for Stmt { - fn from(payload: StmtAugAssign) -> Self { - Stmt::AugAssign(payload) - } -} -impl From> for Ast { - fn from(payload: StmtAugAssign) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [AnnAssign](https://docs.python.org/3/library/ast.html#ast.AnnAssign) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAnnAssign { - pub range: R, - pub target: Box>, - pub annotation: Box>, - pub value: Option>>, - pub simple: bool, -} - -impl Node for StmtAnnAssign { - const NAME: &'static str = "AnnAssign"; - const FIELD_NAMES: &'static [&'static str] = &["target", "annotation", "value", "simple"]; -} -impl From> for Stmt { - fn from(payload: StmtAnnAssign) -> Self { - Stmt::AnnAssign(payload) - } -} -impl From> for Ast { - fn from(payload: StmtAnnAssign) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [For](https://docs.python.org/3/library/ast.html#ast.For) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtFor { - pub range: R, - pub target: Box>, - pub iter: Box>, - pub body: Vec>, - pub orelse: Vec>, - pub type_comment: Option, -} - -impl Node for StmtFor { - const NAME: &'static str = "For"; - const FIELD_NAMES: &'static [&'static str] = - &["target", "iter", "body", "orelse", "type_comment"]; -} -impl From> for Stmt { - fn from(payload: StmtFor) -> Self { - Stmt::For(payload) - } -} -impl From> for Ast { - fn from(payload: StmtFor) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [AsyncFor](https://docs.python.org/3/library/ast.html#ast.AsyncFor) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAsyncFor { - pub range: R, - pub target: Box>, - pub iter: Box>, - pub body: Vec>, - pub orelse: Vec>, - pub type_comment: Option, -} - -impl Node for StmtAsyncFor { - const NAME: &'static str = "AsyncFor"; - const FIELD_NAMES: &'static [&'static str] = - &["target", "iter", "body", "orelse", "type_comment"]; -} -impl From> for Stmt { - fn from(payload: StmtAsyncFor) -> Self { - Stmt::AsyncFor(payload) - } -} -impl From> for Ast { - fn from(payload: StmtAsyncFor) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [While](https://docs.python.org/3/library/ast.html#ast.While) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtWhile { - pub range: R, - pub test: Box>, - pub body: Vec>, - pub orelse: Vec>, -} - -impl Node for StmtWhile { - const NAME: &'static str = "While"; - const FIELD_NAMES: &'static [&'static str] = &["test", "body", "orelse"]; -} -impl From> for Stmt { - fn from(payload: StmtWhile) -> Self { - Stmt::While(payload) - } -} -impl From> for Ast { - fn from(payload: StmtWhile) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [If](https://docs.python.org/3/library/ast.html#ast.If) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtIf { - pub range: R, - pub test: Box>, - pub body: Vec>, - pub orelse: Vec>, -} - -impl Node for StmtIf { - const NAME: &'static str = "If"; - const FIELD_NAMES: &'static [&'static str] = &["test", "body", "orelse"]; -} -impl From> for Stmt { - fn from(payload: StmtIf) -> Self { - Stmt::If(payload) - } -} -impl From> for Ast { - fn from(payload: StmtIf) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [With](https://docs.python.org/3/library/ast.html#ast.With) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtWith { - pub range: R, - pub items: Vec>, - pub body: Vec>, - pub type_comment: Option, -} - -impl Node for StmtWith { - const NAME: &'static str = "With"; - const FIELD_NAMES: &'static [&'static str] = &["items", "body", "type_comment"]; -} -impl From> for Stmt { - fn from(payload: StmtWith) -> Self { - Stmt::With(payload) - } -} -impl From> for Ast { - fn from(payload: StmtWith) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [AsyncWith](https://docs.python.org/3/library/ast.html#ast.AsyncWith) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAsyncWith { - pub range: R, - pub items: Vec>, - pub body: Vec>, - pub type_comment: Option, -} - -impl Node for StmtAsyncWith { - const NAME: &'static str = "AsyncWith"; - const FIELD_NAMES: &'static [&'static str] = &["items", "body", "type_comment"]; -} -impl From> for Stmt { - fn from(payload: StmtAsyncWith) -> Self { - Stmt::AsyncWith(payload) - } -} -impl From> for Ast { - fn from(payload: StmtAsyncWith) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Match](https://docs.python.org/3/library/ast.html#ast.Match) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtMatch { - pub range: R, - pub subject: Box>, - pub cases: Vec>, -} - -impl Node for StmtMatch { - const NAME: &'static str = "Match"; - const FIELD_NAMES: &'static [&'static str] = &["subject", "cases"]; -} -impl From> for Stmt { - fn from(payload: StmtMatch) -> Self { - Stmt::Match(payload) - } -} -impl From> for Ast { - fn from(payload: StmtMatch) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Raise](https://docs.python.org/3/library/ast.html#ast.Raise) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtRaise { - pub range: R, - pub exc: Option>>, - pub cause: Option>>, -} - -impl Node for StmtRaise { - const NAME: &'static str = "Raise"; - const FIELD_NAMES: &'static [&'static str] = &["exc", "cause"]; -} -impl From> for Stmt { - fn from(payload: StmtRaise) -> Self { - Stmt::Raise(payload) - } -} -impl From> for Ast { - fn from(payload: StmtRaise) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Try](https://docs.python.org/3/library/ast.html#ast.Try) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtTry { - pub range: R, - pub body: Vec>, - pub handlers: Vec>, - pub orelse: Vec>, - pub finalbody: Vec>, -} - -impl Node for StmtTry { - const NAME: &'static str = "Try"; - const FIELD_NAMES: &'static [&'static str] = &["body", "handlers", "orelse", "finalbody"]; -} -impl From> for Stmt { - fn from(payload: StmtTry) -> Self { - Stmt::Try(payload) - } -} -impl From> for Ast { - fn from(payload: StmtTry) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [TryStar](https://docs.python.org/3/library/ast.html#ast.TryStar) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtTryStar { - pub range: R, - pub body: Vec>, - pub handlers: Vec>, - pub orelse: Vec>, - pub finalbody: Vec>, -} - -impl Node for StmtTryStar { - const NAME: &'static str = "TryStar"; - const FIELD_NAMES: &'static [&'static str] = &["body", "handlers", "orelse", "finalbody"]; -} -impl From> for Stmt { - fn from(payload: StmtTryStar) -> Self { - Stmt::TryStar(payload) - } -} -impl From> for Ast { - fn from(payload: StmtTryStar) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Assert](https://docs.python.org/3/library/ast.html#ast.Assert) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAssert { - pub range: R, - pub test: Box>, - pub msg: Option>>, -} - -impl Node for StmtAssert { - const NAME: &'static str = "Assert"; - const FIELD_NAMES: &'static [&'static str] = &["test", "msg"]; -} -impl From> for Stmt { - fn from(payload: StmtAssert) -> Self { - Stmt::Assert(payload) - } -} -impl From> for Ast { - fn from(payload: StmtAssert) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Import](https://docs.python.org/3/library/ast.html#ast.Import) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtImport { - pub range: R, - pub names: Vec>, -} - -impl Node for StmtImport { - const NAME: &'static str = "Import"; - const FIELD_NAMES: &'static [&'static str] = &["names"]; -} -impl From> for Stmt { - fn from(payload: StmtImport) -> Self { - Stmt::Import(payload) - } -} -impl From> for Ast { - fn from(payload: StmtImport) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [ImportFrom](https://docs.python.org/3/library/ast.html#ast.ImportFrom) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtImportFrom { - pub range: R, - pub module: Option, - pub names: Vec>, - pub level: Option, -} - -impl Node for StmtImportFrom { - const NAME: &'static str = "ImportFrom"; - const FIELD_NAMES: &'static [&'static str] = &["module", "names", "level"]; -} -impl From> for Stmt { - fn from(payload: StmtImportFrom) -> Self { - Stmt::ImportFrom(payload) - } -} -impl From> for Ast { - fn from(payload: StmtImportFrom) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Global](https://docs.python.org/3/library/ast.html#ast.Global) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtGlobal { - pub range: R, - pub names: Vec, -} - -impl Node for StmtGlobal { - const NAME: &'static str = "Global"; - const FIELD_NAMES: &'static [&'static str] = &["names"]; -} -impl From> for Stmt { - fn from(payload: StmtGlobal) -> Self { - Stmt::Global(payload) - } -} -impl From> for Ast { - fn from(payload: StmtGlobal) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Nonlocal](https://docs.python.org/3/library/ast.html#ast.Nonlocal) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtNonlocal { - pub range: R, - pub names: Vec, -} - -impl Node for StmtNonlocal { - const NAME: &'static str = "Nonlocal"; - const FIELD_NAMES: &'static [&'static str] = &["names"]; -} -impl From> for Stmt { - fn from(payload: StmtNonlocal) -> Self { - Stmt::Nonlocal(payload) - } -} -impl From> for Ast { - fn from(payload: StmtNonlocal) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Expr](https://docs.python.org/3/library/ast.html#ast.Expr) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtExpr { - pub range: R, - pub value: Box>, -} - -impl Node for StmtExpr { - const NAME: &'static str = "Expr"; - const FIELD_NAMES: &'static [&'static str] = &["value"]; -} -impl From> for Stmt { - fn from(payload: StmtExpr) -> Self { - Stmt::Expr(payload) - } -} -impl From> for Ast { - fn from(payload: StmtExpr) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Pass](https://docs.python.org/3/library/ast.html#ast.Pass) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtPass { - pub range: R, -} - -impl Node for StmtPass { - const NAME: &'static str = "Pass"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl From> for Stmt { - fn from(payload: StmtPass) -> Self { - Stmt::Pass(payload) - } -} -impl From> for Ast { - fn from(payload: StmtPass) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Break](https://docs.python.org/3/library/ast.html#ast.Break) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtBreak { - pub range: R, -} - -impl Node for StmtBreak { - const NAME: &'static str = "Break"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl From> for Stmt { - fn from(payload: StmtBreak) -> Self { - Stmt::Break(payload) - } -} -impl From> for Ast { - fn from(payload: StmtBreak) -> Self { - Stmt::from(payload).into() - } -} - -/// See also [Continue](https://docs.python.org/3/library/ast.html#ast.Continue) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtContinue { - pub range: R, -} - -impl Node for StmtContinue { - const NAME: &'static str = "Continue"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl From> for Stmt { - fn from(payload: StmtContinue) -> Self { - Stmt::Continue(payload) - } -} -impl From> for Ast { - fn from(payload: StmtContinue) -> Self { - Stmt::from(payload).into() - } -} - -impl Node for Stmt { - const NAME: &'static str = "stmt"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [expr](https://docs.python.org/3/library/ast.html#ast.expr) -#[derive(Clone, Debug, PartialEq, is_macro::Is)] -pub enum Expr { - #[is(name = "bool_op_expr")] - BoolOp(ExprBoolOp), - #[is(name = "named_expr_expr")] - NamedExpr(ExprNamedExpr), - #[is(name = "bin_op_expr")] - BinOp(ExprBinOp), - #[is(name = "unary_op_expr")] - UnaryOp(ExprUnaryOp), - #[is(name = "lambda_expr")] - Lambda(ExprLambda), - #[is(name = "if_exp_expr")] - IfExp(ExprIfExp), - #[is(name = "dict_expr")] - Dict(ExprDict), - #[is(name = "set_expr")] - Set(ExprSet), - #[is(name = "list_comp_expr")] - ListComp(ExprListComp), - #[is(name = "set_comp_expr")] - SetComp(ExprSetComp), - #[is(name = "dict_comp_expr")] - DictComp(ExprDictComp), - #[is(name = "generator_exp_expr")] - GeneratorExp(ExprGeneratorExp), - #[is(name = "await_expr")] - Await(ExprAwait), - #[is(name = "yield_expr")] - Yield(ExprYield), - #[is(name = "yield_from_expr")] - YieldFrom(ExprYieldFrom), - #[is(name = "compare_expr")] - Compare(ExprCompare), - #[is(name = "call_expr")] - Call(ExprCall), - #[is(name = "formatted_value_expr")] - FormattedValue(ExprFormattedValue), - #[is(name = "joined_str_expr")] - JoinedStr(ExprJoinedStr), - #[is(name = "constant_expr")] - Constant(ExprConstant), - #[is(name = "attribute_expr")] - Attribute(ExprAttribute), - #[is(name = "subscript_expr")] - Subscript(ExprSubscript), - #[is(name = "starred_expr")] - Starred(ExprStarred), - #[is(name = "name_expr")] - Name(ExprName), - #[is(name = "list_expr")] - List(ExprList), - #[is(name = "tuple_expr")] - Tuple(ExprTuple), - #[is(name = "slice_expr")] - Slice(ExprSlice), -} - -/// See also [BoolOp](https://docs.python.org/3/library/ast.html#ast.BoolOp) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprBoolOp { - pub range: R, - pub op: BoolOp, - pub values: Vec>, -} - -impl Node for ExprBoolOp { - const NAME: &'static str = "BoolOp"; - const FIELD_NAMES: &'static [&'static str] = &["op", "values"]; -} -impl From> for Expr { - fn from(payload: ExprBoolOp) -> Self { - Expr::BoolOp(payload) - } -} -impl From> for Ast { - fn from(payload: ExprBoolOp) -> Self { - Expr::from(payload).into() - } -} - -/// See also [NamedExpr](https://docs.python.org/3/library/ast.html#ast.NamedExpr) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprNamedExpr { - pub range: R, - pub target: Box>, - pub value: Box>, -} - -impl Node for ExprNamedExpr { - const NAME: &'static str = "NamedExpr"; - const FIELD_NAMES: &'static [&'static str] = &["target", "value"]; -} -impl From> for Expr { - fn from(payload: ExprNamedExpr) -> Self { - Expr::NamedExpr(payload) - } -} -impl From> for Ast { - fn from(payload: ExprNamedExpr) -> Self { - Expr::from(payload).into() - } -} - -/// See also [BinOp](https://docs.python.org/3/library/ast.html#ast.BinOp) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprBinOp { - pub range: R, - pub left: Box>, - pub op: Operator, - pub right: Box>, -} - -impl Node for ExprBinOp { - const NAME: &'static str = "BinOp"; - const FIELD_NAMES: &'static [&'static str] = &["left", "op", "right"]; -} -impl From> for Expr { - fn from(payload: ExprBinOp) -> Self { - Expr::BinOp(payload) - } -} -impl From> for Ast { - fn from(payload: ExprBinOp) -> Self { - Expr::from(payload).into() - } -} - -/// See also [UnaryOp](https://docs.python.org/3/library/ast.html#ast.UnaryOp) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprUnaryOp { - pub range: R, - pub op: UnaryOp, - pub operand: Box>, -} - -impl Node for ExprUnaryOp { - const NAME: &'static str = "UnaryOp"; - const FIELD_NAMES: &'static [&'static str] = &["op", "operand"]; -} -impl From> for Expr { - fn from(payload: ExprUnaryOp) -> Self { - Expr::UnaryOp(payload) - } -} -impl From> for Ast { - fn from(payload: ExprUnaryOp) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Lambda](https://docs.python.org/3/library/ast.html#ast.Lambda) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprLambda { - pub range: R, - pub args: Box>, - pub body: Box>, -} - -impl Node for ExprLambda { - const NAME: &'static str = "Lambda"; - const FIELD_NAMES: &'static [&'static str] = &["args", "body"]; -} -impl From> for Expr { - fn from(payload: ExprLambda) -> Self { - Expr::Lambda(payload) - } -} -impl From> for Ast { - fn from(payload: ExprLambda) -> Self { - Expr::from(payload).into() - } -} - -/// See also [IfExp](https://docs.python.org/3/library/ast.html#ast.IfExp) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprIfExp { - pub range: R, - pub test: Box>, - pub body: Box>, - pub orelse: Box>, -} - -impl Node for ExprIfExp { - const NAME: &'static str = "IfExp"; - const FIELD_NAMES: &'static [&'static str] = &["test", "body", "orelse"]; -} -impl From> for Expr { - fn from(payload: ExprIfExp) -> Self { - Expr::IfExp(payload) - } -} -impl From> for Ast { - fn from(payload: ExprIfExp) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Dict](https://docs.python.org/3/library/ast.html#ast.Dict) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprDict { - pub range: R, - pub keys: Vec>>, - pub values: Vec>, -} - -impl Node for ExprDict { - const NAME: &'static str = "Dict"; - const FIELD_NAMES: &'static [&'static str] = &["keys", "values"]; -} -impl From> for Expr { - fn from(payload: ExprDict) -> Self { - Expr::Dict(payload) - } -} -impl From> for Ast { - fn from(payload: ExprDict) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Set](https://docs.python.org/3/library/ast.html#ast.Set) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprSet { - pub range: R, - pub elts: Vec>, -} - -impl Node for ExprSet { - const NAME: &'static str = "Set"; - const FIELD_NAMES: &'static [&'static str] = &["elts"]; -} -impl From> for Expr { - fn from(payload: ExprSet) -> Self { - Expr::Set(payload) - } -} -impl From> for Ast { - fn from(payload: ExprSet) -> Self { - Expr::from(payload).into() - } -} - -/// See also [ListComp](https://docs.python.org/3/library/ast.html#ast.ListComp) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprListComp { - pub range: R, - pub elt: Box>, - pub generators: Vec>, -} - -impl Node for ExprListComp { - const NAME: &'static str = "ListComp"; - const FIELD_NAMES: &'static [&'static str] = &["elt", "generators"]; -} -impl From> for Expr { - fn from(payload: ExprListComp) -> Self { - Expr::ListComp(payload) - } -} -impl From> for Ast { - fn from(payload: ExprListComp) -> Self { - Expr::from(payload).into() - } -} - -/// See also [SetComp](https://docs.python.org/3/library/ast.html#ast.SetComp) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprSetComp { - pub range: R, - pub elt: Box>, - pub generators: Vec>, -} - -impl Node for ExprSetComp { - const NAME: &'static str = "SetComp"; - const FIELD_NAMES: &'static [&'static str] = &["elt", "generators"]; -} -impl From> for Expr { - fn from(payload: ExprSetComp) -> Self { - Expr::SetComp(payload) - } -} -impl From> for Ast { - fn from(payload: ExprSetComp) -> Self { - Expr::from(payload).into() - } -} - -/// See also [DictComp](https://docs.python.org/3/library/ast.html#ast.DictComp) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprDictComp { - pub range: R, - pub key: Box>, - pub value: Box>, - pub generators: Vec>, -} - -impl Node for ExprDictComp { - const NAME: &'static str = "DictComp"; - const FIELD_NAMES: &'static [&'static str] = &["key", "value", "generators"]; -} -impl From> for Expr { - fn from(payload: ExprDictComp) -> Self { - Expr::DictComp(payload) - } -} -impl From> for Ast { - fn from(payload: ExprDictComp) -> Self { - Expr::from(payload).into() - } -} - -/// See also [GeneratorExp](https://docs.python.org/3/library/ast.html#ast.GeneratorExp) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprGeneratorExp { - pub range: R, - pub elt: Box>, - pub generators: Vec>, -} - -impl Node for ExprGeneratorExp { - const NAME: &'static str = "GeneratorExp"; - const FIELD_NAMES: &'static [&'static str] = &["elt", "generators"]; -} -impl From> for Expr { - fn from(payload: ExprGeneratorExp) -> Self { - Expr::GeneratorExp(payload) - } -} -impl From> for Ast { - fn from(payload: ExprGeneratorExp) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Await](https://docs.python.org/3/library/ast.html#ast.Await) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprAwait { - pub range: R, - pub value: Box>, -} - -impl Node for ExprAwait { - const NAME: &'static str = "Await"; - const FIELD_NAMES: &'static [&'static str] = &["value"]; -} -impl From> for Expr { - fn from(payload: ExprAwait) -> Self { - Expr::Await(payload) - } -} -impl From> for Ast { - fn from(payload: ExprAwait) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Yield](https://docs.python.org/3/library/ast.html#ast.Yield) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprYield { - pub range: R, - pub value: Option>>, -} - -impl Node for ExprYield { - const NAME: &'static str = "Yield"; - const FIELD_NAMES: &'static [&'static str] = &["value"]; -} -impl From> for Expr { - fn from(payload: ExprYield) -> Self { - Expr::Yield(payload) - } -} -impl From> for Ast { - fn from(payload: ExprYield) -> Self { - Expr::from(payload).into() - } -} - -/// See also [YieldFrom](https://docs.python.org/3/library/ast.html#ast.YieldFrom) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprYieldFrom { - pub range: R, - pub value: Box>, -} - -impl Node for ExprYieldFrom { - const NAME: &'static str = "YieldFrom"; - const FIELD_NAMES: &'static [&'static str] = &["value"]; -} -impl From> for Expr { - fn from(payload: ExprYieldFrom) -> Self { - Expr::YieldFrom(payload) - } -} -impl From> for Ast { - fn from(payload: ExprYieldFrom) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Compare](https://docs.python.org/3/library/ast.html#ast.Compare) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprCompare { - pub range: R, - pub left: Box>, - pub ops: Vec, - pub comparators: Vec>, -} - -impl Node for ExprCompare { - const NAME: &'static str = "Compare"; - const FIELD_NAMES: &'static [&'static str] = &["left", "ops", "comparators"]; -} -impl From> for Expr { - fn from(payload: ExprCompare) -> Self { - Expr::Compare(payload) - } -} -impl From> for Ast { - fn from(payload: ExprCompare) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Call](https://docs.python.org/3/library/ast.html#ast.Call) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprCall { - pub range: R, - pub func: Box>, - pub args: Vec>, - pub keywords: Vec>, -} - -impl Node for ExprCall { - const NAME: &'static str = "Call"; - const FIELD_NAMES: &'static [&'static str] = &["func", "args", "keywords"]; -} -impl From> for Expr { - fn from(payload: ExprCall) -> Self { - Expr::Call(payload) - } -} -impl From> for Ast { - fn from(payload: ExprCall) -> Self { - Expr::from(payload).into() - } -} - -/// See also [FormattedValue](https://docs.python.org/3/library/ast.html#ast.FormattedValue) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprFormattedValue { - pub range: R, - pub value: Box>, - pub conversion: ConversionFlag, - pub format_spec: Option>>, -} - -impl Node for ExprFormattedValue { - const NAME: &'static str = "FormattedValue"; - const FIELD_NAMES: &'static [&'static str] = &["value", "conversion", "format_spec"]; -} -impl From> for Expr { - fn from(payload: ExprFormattedValue) -> Self { - Expr::FormattedValue(payload) - } -} -impl From> for Ast { - fn from(payload: ExprFormattedValue) -> Self { - Expr::from(payload).into() - } -} - -/// See also [JoinedStr](https://docs.python.org/3/library/ast.html#ast.JoinedStr) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprJoinedStr { - pub range: R, - pub values: Vec>, -} - -impl Node for ExprJoinedStr { - const NAME: &'static str = "JoinedStr"; - const FIELD_NAMES: &'static [&'static str] = &["values"]; -} -impl From> for Expr { - fn from(payload: ExprJoinedStr) -> Self { - Expr::JoinedStr(payload) - } -} -impl From> for Ast { - fn from(payload: ExprJoinedStr) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Constant](https://docs.python.org/3/library/ast.html#ast.Constant) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprConstant { - pub range: R, - pub value: Constant, - pub kind: Option, -} - -impl Node for ExprConstant { - const NAME: &'static str = "Constant"; - const FIELD_NAMES: &'static [&'static str] = &["value", "kind"]; -} -impl From> for Expr { - fn from(payload: ExprConstant) -> Self { - Expr::Constant(payload) - } -} -impl From> for Ast { - fn from(payload: ExprConstant) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Attribute](https://docs.python.org/3/library/ast.html#ast.Attribute) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprAttribute { - pub range: R, - pub value: Box>, - pub attr: Identifier, - pub ctx: ExprContext, -} - -impl Node for ExprAttribute { - const NAME: &'static str = "Attribute"; - const FIELD_NAMES: &'static [&'static str] = &["value", "attr", "ctx"]; -} -impl From> for Expr { - fn from(payload: ExprAttribute) -> Self { - Expr::Attribute(payload) - } -} -impl From> for Ast { - fn from(payload: ExprAttribute) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Subscript](https://docs.python.org/3/library/ast.html#ast.Subscript) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprSubscript { - pub range: R, - pub value: Box>, - pub slice: Box>, - pub ctx: ExprContext, -} - -impl Node for ExprSubscript { - const NAME: &'static str = "Subscript"; - const FIELD_NAMES: &'static [&'static str] = &["value", "slice", "ctx"]; -} -impl From> for Expr { - fn from(payload: ExprSubscript) -> Self { - Expr::Subscript(payload) - } -} -impl From> for Ast { - fn from(payload: ExprSubscript) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Starred](https://docs.python.org/3/library/ast.html#ast.Starred) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprStarred { - pub range: R, - pub value: Box>, - pub ctx: ExprContext, -} - -impl Node for ExprStarred { - const NAME: &'static str = "Starred"; - const FIELD_NAMES: &'static [&'static str] = &["value", "ctx"]; -} -impl From> for Expr { - fn from(payload: ExprStarred) -> Self { - Expr::Starred(payload) - } -} -impl From> for Ast { - fn from(payload: ExprStarred) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Name](https://docs.python.org/3/library/ast.html#ast.Name) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprName { - pub range: R, - pub id: Identifier, - pub ctx: ExprContext, -} - -impl Node for ExprName { - const NAME: &'static str = "Name"; - const FIELD_NAMES: &'static [&'static str] = &["id", "ctx"]; -} -impl From> for Expr { - fn from(payload: ExprName) -> Self { - Expr::Name(payload) - } -} -impl From> for Ast { - fn from(payload: ExprName) -> Self { - Expr::from(payload).into() - } -} - -/// See also [List](https://docs.python.org/3/library/ast.html#ast.List) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprList { - pub range: R, - pub elts: Vec>, - pub ctx: ExprContext, -} - -impl Node for ExprList { - const NAME: &'static str = "List"; - const FIELD_NAMES: &'static [&'static str] = &["elts", "ctx"]; -} -impl From> for Expr { - fn from(payload: ExprList) -> Self { - Expr::List(payload) - } -} -impl From> for Ast { - fn from(payload: ExprList) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Tuple](https://docs.python.org/3/library/ast.html#ast.Tuple) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprTuple { - pub range: R, - pub elts: Vec>, - pub ctx: ExprContext, -} - -impl Node for ExprTuple { - const NAME: &'static str = "Tuple"; - const FIELD_NAMES: &'static [&'static str] = &["elts", "ctx"]; -} -impl From> for Expr { - fn from(payload: ExprTuple) -> Self { - Expr::Tuple(payload) - } -} -impl From> for Ast { - fn from(payload: ExprTuple) -> Self { - Expr::from(payload).into() - } -} - -/// See also [Slice](https://docs.python.org/3/library/ast.html#ast.Slice) -#[derive(Clone, Debug, PartialEq)] -pub struct ExprSlice { - pub range: R, - pub lower: Option>>, - pub upper: Option>>, - pub step: Option>>, -} - -impl Node for ExprSlice { - const NAME: &'static str = "Slice"; - const FIELD_NAMES: &'static [&'static str] = &["lower", "upper", "step"]; -} -impl From> for Expr { - fn from(payload: ExprSlice) -> Self { - Expr::Slice(payload) - } -} -impl From> for Ast { - fn from(payload: ExprSlice) -> Self { - Expr::from(payload).into() - } -} - -impl Node for Expr { - const NAME: &'static str = "expr"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [expr_context](https://docs.python.org/3/library/ast.html#ast.expr_context) -#[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] -pub enum ExprContext { - Load, - Store, - Del, -} -impl ExprContext { - #[inline] - pub const fn load(&self) -> Option { - match self { - ExprContext::Load => Some(ExprContextLoad), - _ => None, - } - } - - #[inline] - pub const fn store(&self) -> Option { - match self { - ExprContext::Store => Some(ExprContextStore), - _ => None, - } - } - - #[inline] - pub const fn del(&self) -> Option { - match self { - ExprContext::Del => Some(ExprContextDel), - _ => None, - } - } -} - -pub struct ExprContextLoad; -impl From for ExprContext { - fn from(_: ExprContextLoad) -> Self { - ExprContext::Load - } -} -impl From for Ast { - fn from(_: ExprContextLoad) -> Self { - ExprContext::Load.into() - } -} -impl Node for ExprContextLoad { - const NAME: &'static str = "Load"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for ExprContextLoad { - #[inline] - fn eq(&self, other: &ExprContext) -> bool { - matches!(other, ExprContext::Load) - } -} - -pub struct ExprContextStore; -impl From for ExprContext { - fn from(_: ExprContextStore) -> Self { - ExprContext::Store - } -} -impl From for Ast { - fn from(_: ExprContextStore) -> Self { - ExprContext::Store.into() - } -} -impl Node for ExprContextStore { - const NAME: &'static str = "Store"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for ExprContextStore { - #[inline] - fn eq(&self, other: &ExprContext) -> bool { - matches!(other, ExprContext::Store) - } -} - -pub struct ExprContextDel; -impl From for ExprContext { - fn from(_: ExprContextDel) -> Self { - ExprContext::Del - } -} -impl From for Ast { - fn from(_: ExprContextDel) -> Self { - ExprContext::Del.into() - } -} -impl Node for ExprContextDel { - const NAME: &'static str = "Del"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for ExprContextDel { - #[inline] - fn eq(&self, other: &ExprContext) -> bool { - matches!(other, ExprContext::Del) - } -} - -impl Node for ExprContext { - const NAME: &'static str = "expr_context"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [boolop](https://docs.python.org/3/library/ast.html#ast.boolop) -#[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] -pub enum BoolOp { - And, - Or, -} -impl BoolOp { - #[inline] - pub const fn and(&self) -> Option { - match self { - BoolOp::And => Some(BoolOpAnd), - _ => None, - } - } - - #[inline] - pub const fn or(&self) -> Option { - match self { - BoolOp::Or => Some(BoolOpOr), - _ => None, - } - } -} - -pub struct BoolOpAnd; -impl From for BoolOp { - fn from(_: BoolOpAnd) -> Self { - BoolOp::And - } -} -impl From for Ast { - fn from(_: BoolOpAnd) -> Self { - BoolOp::And.into() - } -} -impl Node for BoolOpAnd { - const NAME: &'static str = "And"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for BoolOpAnd { - #[inline] - fn eq(&self, other: &BoolOp) -> bool { - matches!(other, BoolOp::And) - } -} - -pub struct BoolOpOr; -impl From for BoolOp { - fn from(_: BoolOpOr) -> Self { - BoolOp::Or - } -} -impl From for Ast { - fn from(_: BoolOpOr) -> Self { - BoolOp::Or.into() - } -} -impl Node for BoolOpOr { - const NAME: &'static str = "Or"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for BoolOpOr { - #[inline] - fn eq(&self, other: &BoolOp) -> bool { - matches!(other, BoolOp::Or) - } -} - -impl Node for BoolOp { - const NAME: &'static str = "boolop"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [operator](https://docs.python.org/3/library/ast.html#ast.operator) -#[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] -pub enum Operator { - Add, - Sub, - Mult, - MatMult, - Div, - Mod, - Pow, - LShift, - RShift, - BitOr, - BitXor, - BitAnd, - FloorDiv, -} -impl Operator { - #[inline] - pub const fn operator_add(&self) -> Option { - match self { - Operator::Add => Some(OperatorAdd), - _ => None, - } - } - - #[inline] - pub const fn operator_sub(&self) -> Option { - match self { - Operator::Sub => Some(OperatorSub), - _ => None, - } - } - - #[inline] - pub const fn operator_mult(&self) -> Option { - match self { - Operator::Mult => Some(OperatorMult), - _ => None, - } - } - - #[inline] - pub const fn operator_mat_mult(&self) -> Option { - match self { - Operator::MatMult => Some(OperatorMatMult), - _ => None, - } - } - - #[inline] - pub const fn operator_div(&self) -> Option { - match self { - Operator::Div => Some(OperatorDiv), - _ => None, - } - } - - #[inline] - pub const fn operator_mod(&self) -> Option { - match self { - Operator::Mod => Some(OperatorMod), - _ => None, - } - } - - #[inline] - pub const fn operator_pow(&self) -> Option { - match self { - Operator::Pow => Some(OperatorPow), - _ => None, - } - } - - #[inline] - pub const fn operator_l_shift(&self) -> Option { - match self { - Operator::LShift => Some(OperatorLShift), - _ => None, - } - } - - #[inline] - pub const fn operator_r_shift(&self) -> Option { - match self { - Operator::RShift => Some(OperatorRShift), - _ => None, - } - } - - #[inline] - pub const fn operator_bit_or(&self) -> Option { - match self { - Operator::BitOr => Some(OperatorBitOr), - _ => None, - } - } - - #[inline] - pub const fn operator_bit_xor(&self) -> Option { - match self { - Operator::BitXor => Some(OperatorBitXor), - _ => None, - } - } - - #[inline] - pub const fn operator_bit_and(&self) -> Option { - match self { - Operator::BitAnd => Some(OperatorBitAnd), - _ => None, - } - } - - #[inline] - pub const fn operator_floor_div(&self) -> Option { - match self { - Operator::FloorDiv => Some(OperatorFloorDiv), - _ => None, - } - } -} - -pub struct OperatorAdd; -impl From for Operator { - fn from(_: OperatorAdd) -> Self { - Operator::Add - } -} -impl From for Ast { - fn from(_: OperatorAdd) -> Self { - Operator::Add.into() - } -} -impl Node for OperatorAdd { - const NAME: &'static str = "Add"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorAdd { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::Add) - } -} - -pub struct OperatorSub; -impl From for Operator { - fn from(_: OperatorSub) -> Self { - Operator::Sub - } -} -impl From for Ast { - fn from(_: OperatorSub) -> Self { - Operator::Sub.into() - } -} -impl Node for OperatorSub { - const NAME: &'static str = "Sub"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorSub { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::Sub) - } -} - -pub struct OperatorMult; -impl From for Operator { - fn from(_: OperatorMult) -> Self { - Operator::Mult - } -} -impl From for Ast { - fn from(_: OperatorMult) -> Self { - Operator::Mult.into() - } -} -impl Node for OperatorMult { - const NAME: &'static str = "Mult"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorMult { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::Mult) - } -} - -pub struct OperatorMatMult; -impl From for Operator { - fn from(_: OperatorMatMult) -> Self { - Operator::MatMult - } -} -impl From for Ast { - fn from(_: OperatorMatMult) -> Self { - Operator::MatMult.into() - } -} -impl Node for OperatorMatMult { - const NAME: &'static str = "MatMult"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorMatMult { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::MatMult) - } -} - -pub struct OperatorDiv; -impl From for Operator { - fn from(_: OperatorDiv) -> Self { - Operator::Div - } -} -impl From for Ast { - fn from(_: OperatorDiv) -> Self { - Operator::Div.into() - } -} -impl Node for OperatorDiv { - const NAME: &'static str = "Div"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorDiv { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::Div) - } -} - -pub struct OperatorMod; -impl From for Operator { - fn from(_: OperatorMod) -> Self { - Operator::Mod - } -} -impl From for Ast { - fn from(_: OperatorMod) -> Self { - Operator::Mod.into() - } -} -impl Node for OperatorMod { - const NAME: &'static str = "Mod"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorMod { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::Mod) - } -} - -pub struct OperatorPow; -impl From for Operator { - fn from(_: OperatorPow) -> Self { - Operator::Pow - } -} -impl From for Ast { - fn from(_: OperatorPow) -> Self { - Operator::Pow.into() - } -} -impl Node for OperatorPow { - const NAME: &'static str = "Pow"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorPow { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::Pow) - } -} - -pub struct OperatorLShift; -impl From for Operator { - fn from(_: OperatorLShift) -> Self { - Operator::LShift - } -} -impl From for Ast { - fn from(_: OperatorLShift) -> Self { - Operator::LShift.into() - } -} -impl Node for OperatorLShift { - const NAME: &'static str = "LShift"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorLShift { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::LShift) - } -} - -pub struct OperatorRShift; -impl From for Operator { - fn from(_: OperatorRShift) -> Self { - Operator::RShift - } -} -impl From for Ast { - fn from(_: OperatorRShift) -> Self { - Operator::RShift.into() - } -} -impl Node for OperatorRShift { - const NAME: &'static str = "RShift"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorRShift { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::RShift) - } -} - -pub struct OperatorBitOr; -impl From for Operator { - fn from(_: OperatorBitOr) -> Self { - Operator::BitOr - } -} -impl From for Ast { - fn from(_: OperatorBitOr) -> Self { - Operator::BitOr.into() - } -} -impl Node for OperatorBitOr { - const NAME: &'static str = "BitOr"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorBitOr { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::BitOr) - } -} - -pub struct OperatorBitXor; -impl From for Operator { - fn from(_: OperatorBitXor) -> Self { - Operator::BitXor - } -} -impl From for Ast { - fn from(_: OperatorBitXor) -> Self { - Operator::BitXor.into() - } -} -impl Node for OperatorBitXor { - const NAME: &'static str = "BitXor"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorBitXor { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::BitXor) - } -} - -pub struct OperatorBitAnd; -impl From for Operator { - fn from(_: OperatorBitAnd) -> Self { - Operator::BitAnd - } -} -impl From for Ast { - fn from(_: OperatorBitAnd) -> Self { - Operator::BitAnd.into() - } -} -impl Node for OperatorBitAnd { - const NAME: &'static str = "BitAnd"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorBitAnd { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::BitAnd) - } -} - -pub struct OperatorFloorDiv; -impl From for Operator { - fn from(_: OperatorFloorDiv) -> Self { - Operator::FloorDiv - } -} -impl From for Ast { - fn from(_: OperatorFloorDiv) -> Self { - Operator::FloorDiv.into() - } -} -impl Node for OperatorFloorDiv { - const NAME: &'static str = "FloorDiv"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for OperatorFloorDiv { - #[inline] - fn eq(&self, other: &Operator) -> bool { - matches!(other, Operator::FloorDiv) - } -} - -impl Node for Operator { - const NAME: &'static str = "operator"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [unaryop](https://docs.python.org/3/library/ast.html#ast.unaryop) -#[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] -pub enum UnaryOp { - Invert, - Not, - UAdd, - USub, -} -impl UnaryOp { - #[inline] - pub const fn invert(&self) -> Option { - match self { - UnaryOp::Invert => Some(UnaryOpInvert), - _ => None, - } - } - - #[inline] - pub const fn not(&self) -> Option { - match self { - UnaryOp::Not => Some(UnaryOpNot), - _ => None, - } - } - - #[inline] - pub const fn u_add(&self) -> Option { - match self { - UnaryOp::UAdd => Some(UnaryOpUAdd), - _ => None, - } - } - - #[inline] - pub const fn u_sub(&self) -> Option { - match self { - UnaryOp::USub => Some(UnaryOpUSub), - _ => None, - } - } -} - -pub struct UnaryOpInvert; -impl From for UnaryOp { - fn from(_: UnaryOpInvert) -> Self { - UnaryOp::Invert - } -} -impl From for Ast { - fn from(_: UnaryOpInvert) -> Self { - UnaryOp::Invert.into() - } -} -impl Node for UnaryOpInvert { - const NAME: &'static str = "Invert"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for UnaryOpInvert { - #[inline] - fn eq(&self, other: &UnaryOp) -> bool { - matches!(other, UnaryOp::Invert) - } -} - -pub struct UnaryOpNot; -impl From for UnaryOp { - fn from(_: UnaryOpNot) -> Self { - UnaryOp::Not - } -} -impl From for Ast { - fn from(_: UnaryOpNot) -> Self { - UnaryOp::Not.into() - } -} -impl Node for UnaryOpNot { - const NAME: &'static str = "Not"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for UnaryOpNot { - #[inline] - fn eq(&self, other: &UnaryOp) -> bool { - matches!(other, UnaryOp::Not) - } -} - -pub struct UnaryOpUAdd; -impl From for UnaryOp { - fn from(_: UnaryOpUAdd) -> Self { - UnaryOp::UAdd - } -} -impl From for Ast { - fn from(_: UnaryOpUAdd) -> Self { - UnaryOp::UAdd.into() - } -} -impl Node for UnaryOpUAdd { - const NAME: &'static str = "UAdd"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for UnaryOpUAdd { - #[inline] - fn eq(&self, other: &UnaryOp) -> bool { - matches!(other, UnaryOp::UAdd) - } -} - -pub struct UnaryOpUSub; -impl From for UnaryOp { - fn from(_: UnaryOpUSub) -> Self { - UnaryOp::USub - } -} -impl From for Ast { - fn from(_: UnaryOpUSub) -> Self { - UnaryOp::USub.into() - } -} -impl Node for UnaryOpUSub { - const NAME: &'static str = "USub"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for UnaryOpUSub { - #[inline] - fn eq(&self, other: &UnaryOp) -> bool { - matches!(other, UnaryOp::USub) - } -} - -impl Node for UnaryOp { - const NAME: &'static str = "unaryop"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [cmpop](https://docs.python.org/3/library/ast.html#ast.cmpop) -#[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] -pub enum CmpOp { - Eq, - NotEq, - Lt, - LtE, - Gt, - GtE, - Is, - IsNot, - In, - NotIn, -} -impl CmpOp { - #[inline] - pub const fn cmp_op_eq(&self) -> Option { - match self { - CmpOp::Eq => Some(CmpOpEq), - _ => None, - } - } - - #[inline] - pub const fn cmp_op_not_eq(&self) -> Option { - match self { - CmpOp::NotEq => Some(CmpOpNotEq), - _ => None, - } - } - - #[inline] - pub const fn cmp_op_lt(&self) -> Option { - match self { - CmpOp::Lt => Some(CmpOpLt), - _ => None, - } - } - - #[inline] - pub const fn cmp_op_lt_e(&self) -> Option { - match self { - CmpOp::LtE => Some(CmpOpLtE), - _ => None, - } - } - - #[inline] - pub const fn cmp_op_gt(&self) -> Option { - match self { - CmpOp::Gt => Some(CmpOpGt), - _ => None, - } - } - - #[inline] - pub const fn cmp_op_gt_e(&self) -> Option { - match self { - CmpOp::GtE => Some(CmpOpGtE), - _ => None, - } - } - - #[inline] - pub const fn cmp_op_is(&self) -> Option { - match self { - CmpOp::Is => Some(CmpOpIs), - _ => None, - } - } - - #[inline] - pub const fn cmp_op_is_not(&self) -> Option { - match self { - CmpOp::IsNot => Some(CmpOpIsNot), - _ => None, - } - } - - #[inline] - pub const fn cmp_op_in(&self) -> Option { - match self { - CmpOp::In => Some(CmpOpIn), - _ => None, - } - } - - #[inline] - pub const fn cmp_op_not_in(&self) -> Option { - match self { - CmpOp::NotIn => Some(CmpOpNotIn), - _ => None, - } - } -} - -pub struct CmpOpEq; -impl From for CmpOp { - fn from(_: CmpOpEq) -> Self { - CmpOp::Eq - } -} -impl From for Ast { - fn from(_: CmpOpEq) -> Self { - CmpOp::Eq.into() - } -} -impl Node for CmpOpEq { - const NAME: &'static str = "Eq"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpEq { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::Eq) - } -} - -pub struct CmpOpNotEq; -impl From for CmpOp { - fn from(_: CmpOpNotEq) -> Self { - CmpOp::NotEq - } -} -impl From for Ast { - fn from(_: CmpOpNotEq) -> Self { - CmpOp::NotEq.into() - } -} -impl Node for CmpOpNotEq { - const NAME: &'static str = "NotEq"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpNotEq { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::NotEq) - } -} - -pub struct CmpOpLt; -impl From for CmpOp { - fn from(_: CmpOpLt) -> Self { - CmpOp::Lt - } -} -impl From for Ast { - fn from(_: CmpOpLt) -> Self { - CmpOp::Lt.into() - } -} -impl Node for CmpOpLt { - const NAME: &'static str = "Lt"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpLt { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::Lt) - } -} - -pub struct CmpOpLtE; -impl From for CmpOp { - fn from(_: CmpOpLtE) -> Self { - CmpOp::LtE - } -} -impl From for Ast { - fn from(_: CmpOpLtE) -> Self { - CmpOp::LtE.into() - } -} -impl Node for CmpOpLtE { - const NAME: &'static str = "LtE"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpLtE { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::LtE) - } -} - -pub struct CmpOpGt; -impl From for CmpOp { - fn from(_: CmpOpGt) -> Self { - CmpOp::Gt - } -} -impl From for Ast { - fn from(_: CmpOpGt) -> Self { - CmpOp::Gt.into() - } -} -impl Node for CmpOpGt { - const NAME: &'static str = "Gt"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpGt { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::Gt) - } -} - -pub struct CmpOpGtE; -impl From for CmpOp { - fn from(_: CmpOpGtE) -> Self { - CmpOp::GtE - } -} -impl From for Ast { - fn from(_: CmpOpGtE) -> Self { - CmpOp::GtE.into() - } -} -impl Node for CmpOpGtE { - const NAME: &'static str = "GtE"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpGtE { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::GtE) - } -} - -pub struct CmpOpIs; -impl From for CmpOp { - fn from(_: CmpOpIs) -> Self { - CmpOp::Is - } -} -impl From for Ast { - fn from(_: CmpOpIs) -> Self { - CmpOp::Is.into() - } -} -impl Node for CmpOpIs { - const NAME: &'static str = "Is"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpIs { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::Is) - } -} - -pub struct CmpOpIsNot; -impl From for CmpOp { - fn from(_: CmpOpIsNot) -> Self { - CmpOp::IsNot - } -} -impl From for Ast { - fn from(_: CmpOpIsNot) -> Self { - CmpOp::IsNot.into() - } -} -impl Node for CmpOpIsNot { - const NAME: &'static str = "IsNot"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpIsNot { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::IsNot) - } -} - -pub struct CmpOpIn; -impl From for CmpOp { - fn from(_: CmpOpIn) -> Self { - CmpOp::In - } -} -impl From for Ast { - fn from(_: CmpOpIn) -> Self { - CmpOp::In.into() - } -} -impl Node for CmpOpIn { - const NAME: &'static str = "In"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpIn { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::In) - } -} - -pub struct CmpOpNotIn; -impl From for CmpOp { - fn from(_: CmpOpNotIn) -> Self { - CmpOp::NotIn - } -} -impl From for Ast { - fn from(_: CmpOpNotIn) -> Self { - CmpOp::NotIn.into() - } -} -impl Node for CmpOpNotIn { - const NAME: &'static str = "NotIn"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} -impl std::cmp::PartialEq for CmpOpNotIn { - #[inline] - fn eq(&self, other: &CmpOp) -> bool { - matches!(other, CmpOp::NotIn) - } -} - -impl Node for CmpOp { - const NAME: &'static str = "cmpop"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [comprehension](https://docs.python.org/3/library/ast.html#ast.comprehension) -#[derive(Clone, Debug, PartialEq)] -pub struct Comprehension { - pub range: OptionalRange, - pub target: Expr, - pub iter: Expr, - pub ifs: Vec>, - pub is_async: bool, -} - -impl Node for Comprehension { - const NAME: &'static str = "comprehension"; - const FIELD_NAMES: &'static [&'static str] = &["target", "iter", "ifs", "is_async"]; -} - -/// See also [excepthandler](https://docs.python.org/3/library/ast.html#ast.excepthandler) -#[derive(Clone, Debug, PartialEq, is_macro::Is)] -pub enum ExceptHandler { - ExceptHandler(ExceptHandlerExceptHandler), -} - -/// See also [ExceptHandler](https://docs.python.org/3/library/ast.html#ast.ExceptHandler) -#[derive(Clone, Debug, PartialEq)] -pub struct ExceptHandlerExceptHandler { - pub range: R, - pub type_: Option>>, - pub name: Option, - pub body: Vec>, -} - -impl Node for ExceptHandlerExceptHandler { - const NAME: &'static str = "ExceptHandler"; - const FIELD_NAMES: &'static [&'static str] = &["type", "name", "body"]; -} -impl From> for ExceptHandler { - fn from(payload: ExceptHandlerExceptHandler) -> Self { - ExceptHandler::ExceptHandler(payload) - } -} -impl From> for Ast { - fn from(payload: ExceptHandlerExceptHandler) -> Self { - ExceptHandler::from(payload).into() - } -} - -impl Node for ExceptHandler { - const NAME: &'static str = "excepthandler"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [arguments](https://docs.python.org/3/library/ast.html#ast.arguments) -#[derive(Clone, Debug, PartialEq)] -pub struct PythonArguments { - pub range: OptionalRange, - pub posonlyargs: Vec>, - pub args: Vec>, - pub vararg: Option>>, - pub kwonlyargs: Vec>, - pub kw_defaults: Vec>, - pub kwarg: Option>>, - pub defaults: Vec>, -} - -impl Node for PythonArguments { - const NAME: &'static str = "arguments"; - const FIELD_NAMES: &'static [&'static str] = &[ - "posonlyargs", - "args", - "vararg", - "kwonlyargs", - "kw_defaults", - "kwarg", - "defaults", - ]; -} - -/// See also [arg](https://docs.python.org/3/library/ast.html#ast.arg) -#[derive(Clone, Debug, PartialEq)] -pub struct Arg { - pub range: R, - pub arg: Identifier, - pub annotation: Option>>, - pub type_comment: Option, -} - -impl Node for Arg { - const NAME: &'static str = "arg"; - const FIELD_NAMES: &'static [&'static str] = &["arg", "annotation", "type_comment"]; -} - -/// See also [keyword](https://docs.python.org/3/library/ast.html#ast.keyword) -#[derive(Clone, Debug, PartialEq)] -pub struct Keyword { - pub range: R, - pub arg: Option, - pub value: Expr, -} - -impl Node for Keyword { - const NAME: &'static str = "keyword"; - const FIELD_NAMES: &'static [&'static str] = &["arg", "value"]; -} - -/// See also [alias](https://docs.python.org/3/library/ast.html#ast.alias) -#[derive(Clone, Debug, PartialEq)] -pub struct Alias { - pub range: R, - pub name: Identifier, - pub asname: Option, -} - -impl Node for Alias { - const NAME: &'static str = "alias"; - const FIELD_NAMES: &'static [&'static str] = &["name", "asname"]; -} - -/// See also [withitem](https://docs.python.org/3/library/ast.html#ast.withitem) -#[derive(Clone, Debug, PartialEq)] -pub struct WithItem { - pub range: OptionalRange, - pub context_expr: Expr, - pub optional_vars: Option>>, -} - -impl Node for WithItem { - const NAME: &'static str = "withitem"; - const FIELD_NAMES: &'static [&'static str] = &["context_expr", "optional_vars"]; -} - -/// See also [match_case](https://docs.python.org/3/library/ast.html#ast.match_case) -#[derive(Clone, Debug, PartialEq)] -pub struct MatchCase { - pub range: OptionalRange, - pub pattern: Pattern, - pub guard: Option>>, - pub body: Vec>, -} - -impl Node for MatchCase { - const NAME: &'static str = "match_case"; - const FIELD_NAMES: &'static [&'static str] = &["pattern", "guard", "body"]; -} - -/// See also [pattern](https://docs.python.org/3/library/ast.html#ast.pattern) -#[derive(Clone, Debug, PartialEq, is_macro::Is)] -pub enum Pattern { - MatchValue(PatternMatchValue), - MatchSingleton(PatternMatchSingleton), - MatchSequence(PatternMatchSequence), - MatchMapping(PatternMatchMapping), - MatchClass(PatternMatchClass), - MatchStar(PatternMatchStar), - MatchAs(PatternMatchAs), - MatchOr(PatternMatchOr), -} - -/// See also [MatchValue](https://docs.python.org/3/library/ast.html#ast.MatchValue) -#[derive(Clone, Debug, PartialEq)] -pub struct PatternMatchValue { - pub range: R, - pub value: Box>, -} - -impl Node for PatternMatchValue { - const NAME: &'static str = "MatchValue"; - const FIELD_NAMES: &'static [&'static str] = &["value"]; -} -impl From> for Pattern { - fn from(payload: PatternMatchValue) -> Self { - Pattern::MatchValue(payload) - } -} -impl From> for Ast { - fn from(payload: PatternMatchValue) -> Self { - Pattern::from(payload).into() - } -} - -/// See also [MatchSingleton](https://docs.python.org/3/library/ast.html#ast.MatchSingleton) -#[derive(Clone, Debug, PartialEq)] -pub struct PatternMatchSingleton { - pub range: R, - pub value: Constant, -} - -impl Node for PatternMatchSingleton { - const NAME: &'static str = "MatchSingleton"; - const FIELD_NAMES: &'static [&'static str] = &["value"]; -} -impl From> for Pattern { - fn from(payload: PatternMatchSingleton) -> Self { - Pattern::MatchSingleton(payload) - } -} -impl From> for Ast { - fn from(payload: PatternMatchSingleton) -> Self { - Pattern::from(payload).into() - } -} - -/// See also [MatchSequence](https://docs.python.org/3/library/ast.html#ast.MatchSequence) -#[derive(Clone, Debug, PartialEq)] -pub struct PatternMatchSequence { - pub range: R, - pub patterns: Vec>, -} - -impl Node for PatternMatchSequence { - const NAME: &'static str = "MatchSequence"; - const FIELD_NAMES: &'static [&'static str] = &["patterns"]; -} -impl From> for Pattern { - fn from(payload: PatternMatchSequence) -> Self { - Pattern::MatchSequence(payload) - } -} -impl From> for Ast { - fn from(payload: PatternMatchSequence) -> Self { - Pattern::from(payload).into() - } -} - -/// See also [MatchMapping](https://docs.python.org/3/library/ast.html#ast.MatchMapping) -#[derive(Clone, Debug, PartialEq)] -pub struct PatternMatchMapping { - pub range: R, - pub keys: Vec>, - pub patterns: Vec>, - pub rest: Option, -} - -impl Node for PatternMatchMapping { - const NAME: &'static str = "MatchMapping"; - const FIELD_NAMES: &'static [&'static str] = &["keys", "patterns", "rest"]; -} -impl From> for Pattern { - fn from(payload: PatternMatchMapping) -> Self { - Pattern::MatchMapping(payload) - } -} -impl From> for Ast { - fn from(payload: PatternMatchMapping) -> Self { - Pattern::from(payload).into() - } -} - -/// See also [MatchClass](https://docs.python.org/3/library/ast.html#ast.MatchClass) -#[derive(Clone, Debug, PartialEq)] -pub struct PatternMatchClass { - pub range: R, - pub cls: Box>, - pub patterns: Vec>, - pub kwd_attrs: Vec, - pub kwd_patterns: Vec>, -} - -impl Node for PatternMatchClass { - const NAME: &'static str = "MatchClass"; - const FIELD_NAMES: &'static [&'static str] = &["cls", "patterns", "kwd_attrs", "kwd_patterns"]; -} -impl From> for Pattern { - fn from(payload: PatternMatchClass) -> Self { - Pattern::MatchClass(payload) - } -} -impl From> for Ast { - fn from(payload: PatternMatchClass) -> Self { - Pattern::from(payload).into() - } -} - -/// See also [MatchStar](https://docs.python.org/3/library/ast.html#ast.MatchStar) -#[derive(Clone, Debug, PartialEq)] -pub struct PatternMatchStar { - pub range: R, - pub name: Option, -} - -impl Node for PatternMatchStar { - const NAME: &'static str = "MatchStar"; - const FIELD_NAMES: &'static [&'static str] = &["name"]; -} -impl From> for Pattern { - fn from(payload: PatternMatchStar) -> Self { - Pattern::MatchStar(payload) - } -} -impl From> for Ast { - fn from(payload: PatternMatchStar) -> Self { - Pattern::from(payload).into() - } -} - -/// See also [MatchAs](https://docs.python.org/3/library/ast.html#ast.MatchAs) -#[derive(Clone, Debug, PartialEq)] -pub struct PatternMatchAs { - pub range: R, - pub pattern: Option>>, - pub name: Option, -} - -impl Node for PatternMatchAs { - const NAME: &'static str = "MatchAs"; - const FIELD_NAMES: &'static [&'static str] = &["pattern", "name"]; -} -impl From> for Pattern { - fn from(payload: PatternMatchAs) -> Self { - Pattern::MatchAs(payload) - } -} -impl From> for Ast { - fn from(payload: PatternMatchAs) -> Self { - Pattern::from(payload).into() - } -} - -/// See also [MatchOr](https://docs.python.org/3/library/ast.html#ast.MatchOr) -#[derive(Clone, Debug, PartialEq)] -pub struct PatternMatchOr { - pub range: R, - pub patterns: Vec>, -} - -impl Node for PatternMatchOr { - const NAME: &'static str = "MatchOr"; - const FIELD_NAMES: &'static [&'static str] = &["patterns"]; -} -impl From> for Pattern { - fn from(payload: PatternMatchOr) -> Self { - Pattern::MatchOr(payload) - } -} -impl From> for Ast { - fn from(payload: PatternMatchOr) -> Self { - Pattern::from(payload).into() - } -} - -impl Node for Pattern { - const NAME: &'static str = "pattern"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [type_ignore](https://docs.python.org/3/library/ast.html#ast.type_ignore) -#[derive(Clone, Debug, PartialEq, is_macro::Is)] -pub enum TypeIgnore { - TypeIgnore(TypeIgnoreTypeIgnore), -} - -/// See also [TypeIgnore](https://docs.python.org/3/library/ast.html#ast.TypeIgnore) -#[derive(Clone, Debug, PartialEq)] -pub struct TypeIgnoreTypeIgnore { - pub range: OptionalRange, - pub lineno: Int, - pub tag: String, -} - -impl Node for TypeIgnoreTypeIgnore { - const NAME: &'static str = "TypeIgnore"; - const FIELD_NAMES: &'static [&'static str] = &["lineno", "tag"]; -} -impl From> for TypeIgnore { - fn from(payload: TypeIgnoreTypeIgnore) -> Self { - TypeIgnore::TypeIgnore(payload) - } -} -impl From> for Ast { - fn from(payload: TypeIgnoreTypeIgnore) -> Self { - TypeIgnore::from(payload).into() - } -} - -impl Node for TypeIgnore { - const NAME: &'static str = "type_ignore"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// See also [type_param](https://docs.python.org/3/library/ast.html#ast.type_param) -#[derive(Clone, Debug, PartialEq, is_macro::Is)] -pub enum TypeParam { - TypeVar(TypeParamTypeVar), - ParamSpec(TypeParamParamSpec), - TypeVarTuple(TypeParamTypeVarTuple), -} - -/// See also [TypeVar](https://docs.python.org/3/library/ast.html#ast.TypeVar) -#[derive(Clone, Debug, PartialEq)] -pub struct TypeParamTypeVar { - pub range: R, - pub name: Identifier, - pub bound: Option>>, -} - -impl Node for TypeParamTypeVar { - const NAME: &'static str = "TypeVar"; - const FIELD_NAMES: &'static [&'static str] = &["name", "bound"]; -} -impl From> for TypeParam { - fn from(payload: TypeParamTypeVar) -> Self { - TypeParam::TypeVar(payload) - } -} -impl From> for Ast { - fn from(payload: TypeParamTypeVar) -> Self { - TypeParam::from(payload).into() - } -} - -/// See also [ParamSpec](https://docs.python.org/3/library/ast.html#ast.ParamSpec) -#[derive(Clone, Debug, PartialEq)] -pub struct TypeParamParamSpec { - pub range: R, - pub name: Identifier, -} - -impl Node for TypeParamParamSpec { - const NAME: &'static str = "ParamSpec"; - const FIELD_NAMES: &'static [&'static str] = &["name"]; -} -impl From> for TypeParam { - fn from(payload: TypeParamParamSpec) -> Self { - TypeParam::ParamSpec(payload) - } -} -impl From> for Ast { - fn from(payload: TypeParamParamSpec) -> Self { - TypeParam::from(payload).into() - } -} - -/// See also [TypeVarTuple](https://docs.python.org/3/library/ast.html#ast.TypeVarTuple) -#[derive(Clone, Debug, PartialEq)] -pub struct TypeParamTypeVarTuple { - pub range: R, - pub name: Identifier, -} - -impl Node for TypeParamTypeVarTuple { - const NAME: &'static str = "TypeVarTuple"; - const FIELD_NAMES: &'static [&'static str] = &["name"]; -} -impl From> for TypeParam { - fn from(payload: TypeParamTypeVarTuple) -> Self { - TypeParam::TypeVarTuple(payload) - } -} -impl From> for Ast { - fn from(payload: TypeParamTypeVarTuple) -> Self { - TypeParam::from(payload).into() - } -} - -impl Node for TypeParam { - const NAME: &'static str = "type_param"; - const FIELD_NAMES: &'static [&'static str] = &[]; -} - -/// An alternative type of AST `arguments`. This is parser-friendly and human-friendly definition of function arguments. -/// This form also has advantage to implement pre-order traverse. -/// `defaults` and `kw_defaults` fields are removed and the default values are placed under each `arg_with_default` typed argument. -/// `vararg` and `kwarg` are still typed as `arg` because they never can have a default value. -/// -/// The matching Python style AST type is [PythonArguments]. While [PythonArguments] has ordered `kwonlyargs` fields by -/// default existence, [Arguments] has location-ordered kwonlyargs fields. -/// -/// NOTE: This type is different from original Python AST. - -#[derive(Clone, Debug, PartialEq)] -pub struct Arguments { - pub range: OptionalRange, - pub posonlyargs: Vec>, - pub args: Vec>, - pub vararg: Option>>, - pub kwonlyargs: Vec>, - pub kwarg: Option>>, -} - -impl Node for Arguments { - const NAME: &'static str = "alt:arguments"; - const FIELD_NAMES: &'static [&'static str] = - &["posonlyargs", "args", "vararg", "kwonlyargs", "kwarg"]; -} - -/// An alternative type of AST `arg`. This is used for each function argument that might have a default value. -/// Used by `Arguments` original type. -/// -/// NOTE: This type is different from original Python AST. - -#[derive(Clone, Debug, PartialEq)] -pub struct ArgWithDefault { - pub range: OptionalRange, - pub def: Arg, - pub default: Option>>, -} - -impl Node for ArgWithDefault { - const NAME: &'static str = "arg_with_default"; - const FIELD_NAMES: &'static [&'static str] = &["def", "default"]; -} diff --git a/ast/src/gen/located.rs b/ast/src/gen/located.rs deleted file mode 100644 index 7376652f..00000000 --- a/ast/src/gen/located.rs +++ /dev/null @@ -1,1478 +0,0 @@ -// File automatically generated by ast/asdl_rs.py. - -pub type Mod = crate::generic::Mod; - -pub type ModModule = crate::generic::ModModule; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for ModModule { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for ModModule { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ModInteractive = crate::generic::ModInteractive; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for ModInteractive { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for ModInteractive { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ModExpression = crate::generic::ModExpression; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for ModExpression { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for ModExpression { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ModFunctionType = crate::generic::ModFunctionType; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for ModFunctionType { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for ModFunctionType { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for Mod { - fn range(&self) -> SourceRange { - match self { - Self::Module(node) => node.range(), - Self::Interactive(node) => node.range(), - Self::Expression(node) => node.range(), - Self::FunctionType(node) => node.range(), - } - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for Mod { - fn range_mut(&mut self) -> &mut SourceRange { - match self { - Self::Module(node) => node.range_mut(), - Self::Interactive(node) => node.range_mut(), - Self::Expression(node) => node.range_mut(), - Self::FunctionType(node) => node.range_mut(), - } - } -} - -pub type Stmt = crate::generic::Stmt; - -pub type StmtFunctionDef = crate::generic::StmtFunctionDef; - -impl Located for StmtFunctionDef { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtFunctionDef { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtAsyncFunctionDef = crate::generic::StmtAsyncFunctionDef; - -impl Located for StmtAsyncFunctionDef { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtAsyncFunctionDef { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtClassDef = crate::generic::StmtClassDef; - -impl Located for StmtClassDef { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtClassDef { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtReturn = crate::generic::StmtReturn; - -impl Located for StmtReturn { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtReturn { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtDelete = crate::generic::StmtDelete; - -impl Located for StmtDelete { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtDelete { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtAssign = crate::generic::StmtAssign; - -impl Located for StmtAssign { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtAssign { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtTypeAlias = crate::generic::StmtTypeAlias; - -impl Located for StmtTypeAlias { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtTypeAlias { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtAugAssign = crate::generic::StmtAugAssign; - -impl Located for StmtAugAssign { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtAugAssign { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtAnnAssign = crate::generic::StmtAnnAssign; - -impl Located for StmtAnnAssign { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtAnnAssign { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtFor = crate::generic::StmtFor; - -impl Located for StmtFor { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtFor { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtAsyncFor = crate::generic::StmtAsyncFor; - -impl Located for StmtAsyncFor { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtAsyncFor { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtWhile = crate::generic::StmtWhile; - -impl Located for StmtWhile { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtWhile { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtIf = crate::generic::StmtIf; - -impl Located for StmtIf { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtIf { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtWith = crate::generic::StmtWith; - -impl Located for StmtWith { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtWith { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtAsyncWith = crate::generic::StmtAsyncWith; - -impl Located for StmtAsyncWith { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtAsyncWith { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtMatch = crate::generic::StmtMatch; - -impl Located for StmtMatch { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtMatch { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtRaise = crate::generic::StmtRaise; - -impl Located for StmtRaise { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtRaise { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtTry = crate::generic::StmtTry; - -impl Located for StmtTry { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtTry { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtTryStar = crate::generic::StmtTryStar; - -impl Located for StmtTryStar { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtTryStar { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtAssert = crate::generic::StmtAssert; - -impl Located for StmtAssert { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtAssert { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtImport = crate::generic::StmtImport; - -impl Located for StmtImport { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtImport { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtImportFrom = crate::generic::StmtImportFrom; - -impl Located for StmtImportFrom { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtImportFrom { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtGlobal = crate::generic::StmtGlobal; - -impl Located for StmtGlobal { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtGlobal { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtNonlocal = crate::generic::StmtNonlocal; - -impl Located for StmtNonlocal { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtNonlocal { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtExpr = crate::generic::StmtExpr; - -impl Located for StmtExpr { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtExpr { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtPass = crate::generic::StmtPass; - -impl Located for StmtPass { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtPass { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtBreak = crate::generic::StmtBreak; - -impl Located for StmtBreak { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtBreak { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type StmtContinue = crate::generic::StmtContinue; - -impl Located for StmtContinue { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for StmtContinue { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -impl Located for Stmt { - fn range(&self) -> SourceRange { - match self { - Self::FunctionDef(node) => node.range(), - Self::AsyncFunctionDef(node) => node.range(), - Self::ClassDef(node) => node.range(), - Self::Return(node) => node.range(), - Self::Delete(node) => node.range(), - Self::Assign(node) => node.range(), - Self::TypeAlias(node) => node.range(), - Self::AugAssign(node) => node.range(), - Self::AnnAssign(node) => node.range(), - Self::For(node) => node.range(), - Self::AsyncFor(node) => node.range(), - Self::While(node) => node.range(), - Self::If(node) => node.range(), - Self::With(node) => node.range(), - Self::AsyncWith(node) => node.range(), - Self::Match(node) => node.range(), - Self::Raise(node) => node.range(), - Self::Try(node) => node.range(), - Self::TryStar(node) => node.range(), - Self::Assert(node) => node.range(), - Self::Import(node) => node.range(), - Self::ImportFrom(node) => node.range(), - Self::Global(node) => node.range(), - Self::Nonlocal(node) => node.range(), - Self::Expr(node) => node.range(), - Self::Pass(node) => node.range(), - Self::Break(node) => node.range(), - Self::Continue(node) => node.range(), - } - } -} - -impl LocatedMut for Stmt { - fn range_mut(&mut self) -> &mut SourceRange { - match self { - Self::FunctionDef(node) => node.range_mut(), - Self::AsyncFunctionDef(node) => node.range_mut(), - Self::ClassDef(node) => node.range_mut(), - Self::Return(node) => node.range_mut(), - Self::Delete(node) => node.range_mut(), - Self::Assign(node) => node.range_mut(), - Self::TypeAlias(node) => node.range_mut(), - Self::AugAssign(node) => node.range_mut(), - Self::AnnAssign(node) => node.range_mut(), - Self::For(node) => node.range_mut(), - Self::AsyncFor(node) => node.range_mut(), - Self::While(node) => node.range_mut(), - Self::If(node) => node.range_mut(), - Self::With(node) => node.range_mut(), - Self::AsyncWith(node) => node.range_mut(), - Self::Match(node) => node.range_mut(), - Self::Raise(node) => node.range_mut(), - Self::Try(node) => node.range_mut(), - Self::TryStar(node) => node.range_mut(), - Self::Assert(node) => node.range_mut(), - Self::Import(node) => node.range_mut(), - Self::ImportFrom(node) => node.range_mut(), - Self::Global(node) => node.range_mut(), - Self::Nonlocal(node) => node.range_mut(), - Self::Expr(node) => node.range_mut(), - Self::Pass(node) => node.range_mut(), - Self::Break(node) => node.range_mut(), - Self::Continue(node) => node.range_mut(), - } - } -} - -pub type Expr = crate::generic::Expr; - -pub type ExprBoolOp = crate::generic::ExprBoolOp; - -impl Located for ExprBoolOp { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprBoolOp { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprNamedExpr = crate::generic::ExprNamedExpr; - -impl Located for ExprNamedExpr { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprNamedExpr { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprBinOp = crate::generic::ExprBinOp; - -impl Located for ExprBinOp { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprBinOp { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprUnaryOp = crate::generic::ExprUnaryOp; - -impl Located for ExprUnaryOp { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprUnaryOp { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprLambda = crate::generic::ExprLambda; - -impl Located for ExprLambda { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprLambda { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprIfExp = crate::generic::ExprIfExp; - -impl Located for ExprIfExp { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprIfExp { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprDict = crate::generic::ExprDict; - -impl Located for ExprDict { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprDict { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprSet = crate::generic::ExprSet; - -impl Located for ExprSet { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprSet { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprListComp = crate::generic::ExprListComp; - -impl Located for ExprListComp { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprListComp { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprSetComp = crate::generic::ExprSetComp; - -impl Located for ExprSetComp { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprSetComp { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprDictComp = crate::generic::ExprDictComp; - -impl Located for ExprDictComp { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprDictComp { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprGeneratorExp = crate::generic::ExprGeneratorExp; - -impl Located for ExprGeneratorExp { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprGeneratorExp { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprAwait = crate::generic::ExprAwait; - -impl Located for ExprAwait { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprAwait { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprYield = crate::generic::ExprYield; - -impl Located for ExprYield { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprYield { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprYieldFrom = crate::generic::ExprYieldFrom; - -impl Located for ExprYieldFrom { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprYieldFrom { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprCompare = crate::generic::ExprCompare; - -impl Located for ExprCompare { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprCompare { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprCall = crate::generic::ExprCall; - -impl Located for ExprCall { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprCall { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprFormattedValue = crate::generic::ExprFormattedValue; - -impl Located for ExprFormattedValue { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprFormattedValue { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprJoinedStr = crate::generic::ExprJoinedStr; - -impl Located for ExprJoinedStr { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprJoinedStr { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprConstant = crate::generic::ExprConstant; - -impl Located for ExprConstant { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprConstant { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprAttribute = crate::generic::ExprAttribute; - -impl Located for ExprAttribute { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprAttribute { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprSubscript = crate::generic::ExprSubscript; - -impl Located for ExprSubscript { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprSubscript { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprStarred = crate::generic::ExprStarred; - -impl Located for ExprStarred { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprStarred { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprName = crate::generic::ExprName; - -impl Located for ExprName { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprName { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprList = crate::generic::ExprList; - -impl Located for ExprList { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprList { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprTuple = crate::generic::ExprTuple; - -impl Located for ExprTuple { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprTuple { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExprSlice = crate::generic::ExprSlice; - -impl Located for ExprSlice { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExprSlice { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -impl Located for Expr { - fn range(&self) -> SourceRange { - match self { - Self::BoolOp(node) => node.range(), - Self::NamedExpr(node) => node.range(), - Self::BinOp(node) => node.range(), - Self::UnaryOp(node) => node.range(), - Self::Lambda(node) => node.range(), - Self::IfExp(node) => node.range(), - Self::Dict(node) => node.range(), - Self::Set(node) => node.range(), - Self::ListComp(node) => node.range(), - Self::SetComp(node) => node.range(), - Self::DictComp(node) => node.range(), - Self::GeneratorExp(node) => node.range(), - Self::Await(node) => node.range(), - Self::Yield(node) => node.range(), - Self::YieldFrom(node) => node.range(), - Self::Compare(node) => node.range(), - Self::Call(node) => node.range(), - Self::FormattedValue(node) => node.range(), - Self::JoinedStr(node) => node.range(), - Self::Constant(node) => node.range(), - Self::Attribute(node) => node.range(), - Self::Subscript(node) => node.range(), - Self::Starred(node) => node.range(), - Self::Name(node) => node.range(), - Self::List(node) => node.range(), - Self::Tuple(node) => node.range(), - Self::Slice(node) => node.range(), - } - } -} - -impl LocatedMut for Expr { - fn range_mut(&mut self) -> &mut SourceRange { - match self { - Self::BoolOp(node) => node.range_mut(), - Self::NamedExpr(node) => node.range_mut(), - Self::BinOp(node) => node.range_mut(), - Self::UnaryOp(node) => node.range_mut(), - Self::Lambda(node) => node.range_mut(), - Self::IfExp(node) => node.range_mut(), - Self::Dict(node) => node.range_mut(), - Self::Set(node) => node.range_mut(), - Self::ListComp(node) => node.range_mut(), - Self::SetComp(node) => node.range_mut(), - Self::DictComp(node) => node.range_mut(), - Self::GeneratorExp(node) => node.range_mut(), - Self::Await(node) => node.range_mut(), - Self::Yield(node) => node.range_mut(), - Self::YieldFrom(node) => node.range_mut(), - Self::Compare(node) => node.range_mut(), - Self::Call(node) => node.range_mut(), - Self::FormattedValue(node) => node.range_mut(), - Self::JoinedStr(node) => node.range_mut(), - Self::Constant(node) => node.range_mut(), - Self::Attribute(node) => node.range_mut(), - Self::Subscript(node) => node.range_mut(), - Self::Starred(node) => node.range_mut(), - Self::Name(node) => node.range_mut(), - Self::List(node) => node.range_mut(), - Self::Tuple(node) => node.range_mut(), - Self::Slice(node) => node.range_mut(), - } - } -} - -pub type ExprContext = crate::generic::ExprContext; - -pub type ExprContextLoad = crate::generic::ExprContextLoad; - -pub type ExprContextStore = crate::generic::ExprContextStore; - -pub type ExprContextDel = crate::generic::ExprContextDel; - -pub type BoolOp = crate::generic::BoolOp; - -pub type BoolOpAnd = crate::generic::BoolOpAnd; - -pub type BoolOpOr = crate::generic::BoolOpOr; - -pub type Operator = crate::generic::Operator; - -pub type OperatorAdd = crate::generic::OperatorAdd; - -pub type OperatorSub = crate::generic::OperatorSub; - -pub type OperatorMult = crate::generic::OperatorMult; - -pub type OperatorMatMult = crate::generic::OperatorMatMult; - -pub type OperatorDiv = crate::generic::OperatorDiv; - -pub type OperatorMod = crate::generic::OperatorMod; - -pub type OperatorPow = crate::generic::OperatorPow; - -pub type OperatorLShift = crate::generic::OperatorLShift; - -pub type OperatorRShift = crate::generic::OperatorRShift; - -pub type OperatorBitOr = crate::generic::OperatorBitOr; - -pub type OperatorBitXor = crate::generic::OperatorBitXor; - -pub type OperatorBitAnd = crate::generic::OperatorBitAnd; - -pub type OperatorFloorDiv = crate::generic::OperatorFloorDiv; - -pub type UnaryOp = crate::generic::UnaryOp; - -pub type UnaryOpInvert = crate::generic::UnaryOpInvert; - -pub type UnaryOpNot = crate::generic::UnaryOpNot; - -pub type UnaryOpUAdd = crate::generic::UnaryOpUAdd; - -pub type UnaryOpUSub = crate::generic::UnaryOpUSub; - -pub type CmpOp = crate::generic::CmpOp; - -pub type CmpOpEq = crate::generic::CmpOpEq; - -pub type CmpOpNotEq = crate::generic::CmpOpNotEq; - -pub type CmpOpLt = crate::generic::CmpOpLt; - -pub type CmpOpLtE = crate::generic::CmpOpLtE; - -pub type CmpOpGt = crate::generic::CmpOpGt; - -pub type CmpOpGtE = crate::generic::CmpOpGtE; - -pub type CmpOpIs = crate::generic::CmpOpIs; - -pub type CmpOpIsNot = crate::generic::CmpOpIsNot; - -pub type CmpOpIn = crate::generic::CmpOpIn; - -pub type CmpOpNotIn = crate::generic::CmpOpNotIn; - -pub type Comprehension = crate::generic::Comprehension; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for Comprehension { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for Comprehension { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ExceptHandler = crate::generic::ExceptHandler; - -pub type ExceptHandlerExceptHandler = crate::generic::ExceptHandlerExceptHandler; - -impl Located for ExceptHandlerExceptHandler { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for ExceptHandlerExceptHandler { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -impl Located for ExceptHandler { - fn range(&self) -> SourceRange { - match self { - Self::ExceptHandler(node) => node.range(), - } - } -} - -impl LocatedMut for ExceptHandler { - fn range_mut(&mut self) -> &mut SourceRange { - match self { - Self::ExceptHandler(node) => node.range_mut(), - } - } -} - -pub type PythonArguments = crate::generic::PythonArguments; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for PythonArguments { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for PythonArguments { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type Arg = crate::generic::Arg; - -impl Located for Arg { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for Arg { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type Keyword = crate::generic::Keyword; - -impl Located for Keyword { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for Keyword { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type Alias = crate::generic::Alias; - -impl Located for Alias { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for Alias { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type WithItem = crate::generic::WithItem; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for WithItem { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for WithItem { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type MatchCase = crate::generic::MatchCase; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for MatchCase { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for MatchCase { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type Pattern = crate::generic::Pattern; - -pub type PatternMatchValue = crate::generic::PatternMatchValue; - -impl Located for PatternMatchValue { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for PatternMatchValue { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type PatternMatchSingleton = crate::generic::PatternMatchSingleton; - -impl Located for PatternMatchSingleton { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for PatternMatchSingleton { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type PatternMatchSequence = crate::generic::PatternMatchSequence; - -impl Located for PatternMatchSequence { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for PatternMatchSequence { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type PatternMatchMapping = crate::generic::PatternMatchMapping; - -impl Located for PatternMatchMapping { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for PatternMatchMapping { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type PatternMatchClass = crate::generic::PatternMatchClass; - -impl Located for PatternMatchClass { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for PatternMatchClass { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type PatternMatchStar = crate::generic::PatternMatchStar; - -impl Located for PatternMatchStar { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for PatternMatchStar { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type PatternMatchAs = crate::generic::PatternMatchAs; - -impl Located for PatternMatchAs { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for PatternMatchAs { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type PatternMatchOr = crate::generic::PatternMatchOr; - -impl Located for PatternMatchOr { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for PatternMatchOr { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -impl Located for Pattern { - fn range(&self) -> SourceRange { - match self { - Self::MatchValue(node) => node.range(), - Self::MatchSingleton(node) => node.range(), - Self::MatchSequence(node) => node.range(), - Self::MatchMapping(node) => node.range(), - Self::MatchClass(node) => node.range(), - Self::MatchStar(node) => node.range(), - Self::MatchAs(node) => node.range(), - Self::MatchOr(node) => node.range(), - } - } -} - -impl LocatedMut for Pattern { - fn range_mut(&mut self) -> &mut SourceRange { - match self { - Self::MatchValue(node) => node.range_mut(), - Self::MatchSingleton(node) => node.range_mut(), - Self::MatchSequence(node) => node.range_mut(), - Self::MatchMapping(node) => node.range_mut(), - Self::MatchClass(node) => node.range_mut(), - Self::MatchStar(node) => node.range_mut(), - Self::MatchAs(node) => node.range_mut(), - Self::MatchOr(node) => node.range_mut(), - } - } -} - -pub type TypeIgnore = crate::generic::TypeIgnore; - -pub type TypeIgnoreTypeIgnore = crate::generic::TypeIgnoreTypeIgnore; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for TypeIgnoreTypeIgnore { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for TypeIgnoreTypeIgnore { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for TypeIgnore { - fn range(&self) -> SourceRange { - match self { - Self::TypeIgnore(node) => node.range(), - } - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for TypeIgnore { - fn range_mut(&mut self) -> &mut SourceRange { - match self { - Self::TypeIgnore(node) => node.range_mut(), - } - } -} - -pub type TypeParam = crate::generic::TypeParam; - -pub type TypeParamTypeVar = crate::generic::TypeParamTypeVar; - -impl Located for TypeParamTypeVar { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for TypeParamTypeVar { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type TypeParamParamSpec = crate::generic::TypeParamParamSpec; - -impl Located for TypeParamParamSpec { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for TypeParamParamSpec { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type TypeParamTypeVarTuple = crate::generic::TypeParamTypeVarTuple; - -impl Located for TypeParamTypeVarTuple { - fn range(&self) -> SourceRange { - self.range - } -} - -impl LocatedMut for TypeParamTypeVarTuple { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -impl Located for TypeParam { - fn range(&self) -> SourceRange { - match self { - Self::TypeVar(node) => node.range(), - Self::ParamSpec(node) => node.range(), - Self::TypeVarTuple(node) => node.range(), - } - } -} - -impl LocatedMut for TypeParam { - fn range_mut(&mut self) -> &mut SourceRange { - match self { - Self::TypeVar(node) => node.range_mut(), - Self::ParamSpec(node) => node.range_mut(), - Self::TypeVarTuple(node) => node.range_mut(), - } - } -} - -pub type Arguments = crate::generic::Arguments; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for Arguments { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for Arguments { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} - -pub type ArgWithDefault = crate::generic::ArgWithDefault; - -#[cfg(feature = "all-nodes-with-ranges")] -impl Located for ArgWithDefault { - fn range(&self) -> SourceRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl LocatedMut for ArgWithDefault { - fn range_mut(&mut self) -> &mut SourceRange { - &mut self.range - } -} diff --git a/ast/src/gen/ranged.rs b/ast/src/gen/ranged.rs deleted file mode 100644 index 5d48ff3d..00000000 --- a/ast/src/gen/ranged.rs +++ /dev/null @@ -1,541 +0,0 @@ -// File automatically generated by ast/asdl_rs.py. - -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::ModModule { - fn range(&self) -> TextRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::ModInteractive { - fn range(&self) -> TextRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::ModExpression { - fn range(&self) -> TextRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::ModFunctionType { - fn range(&self) -> TextRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::Mod { - fn range(&self) -> TextRange { - match self { - Self::Module(node) => node.range(), - Self::Interactive(node) => node.range(), - Self::Expression(node) => node.range(), - Self::FunctionType(node) => node.range(), - } - } -} - -impl Ranged for crate::generic::StmtFunctionDef { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtAsyncFunctionDef { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtClassDef { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtReturn { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtDelete { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtAssign { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtTypeAlias { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtAugAssign { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtAnnAssign { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtFor { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtAsyncFor { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtWhile { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtIf { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtWith { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtAsyncWith { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtMatch { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtRaise { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtTry { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtTryStar { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtAssert { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtImport { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtImportFrom { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtGlobal { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtNonlocal { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtExpr { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtPass { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtBreak { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::StmtContinue { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::Stmt { - fn range(&self) -> TextRange { - match self { - Self::FunctionDef(node) => node.range(), - Self::AsyncFunctionDef(node) => node.range(), - Self::ClassDef(node) => node.range(), - Self::Return(node) => node.range(), - Self::Delete(node) => node.range(), - Self::Assign(node) => node.range(), - Self::TypeAlias(node) => node.range(), - Self::AugAssign(node) => node.range(), - Self::AnnAssign(node) => node.range(), - Self::For(node) => node.range(), - Self::AsyncFor(node) => node.range(), - Self::While(node) => node.range(), - Self::If(node) => node.range(), - Self::With(node) => node.range(), - Self::AsyncWith(node) => node.range(), - Self::Match(node) => node.range(), - Self::Raise(node) => node.range(), - Self::Try(node) => node.range(), - Self::TryStar(node) => node.range(), - Self::Assert(node) => node.range(), - Self::Import(node) => node.range(), - Self::ImportFrom(node) => node.range(), - Self::Global(node) => node.range(), - Self::Nonlocal(node) => node.range(), - Self::Expr(node) => node.range(), - Self::Pass(node) => node.range(), - Self::Break(node) => node.range(), - Self::Continue(node) => node.range(), - } - } -} - -impl Ranged for crate::generic::ExprBoolOp { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprNamedExpr { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprBinOp { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprUnaryOp { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprLambda { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprIfExp { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprDict { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprSet { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprListComp { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprSetComp { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprDictComp { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprGeneratorExp { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprAwait { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprYield { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprYieldFrom { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprCompare { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprCall { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprFormattedValue { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprJoinedStr { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprConstant { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprAttribute { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprSubscript { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprStarred { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprName { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprList { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprTuple { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExprSlice { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::Expr { - fn range(&self) -> TextRange { - match self { - Self::BoolOp(node) => node.range(), - Self::NamedExpr(node) => node.range(), - Self::BinOp(node) => node.range(), - Self::UnaryOp(node) => node.range(), - Self::Lambda(node) => node.range(), - Self::IfExp(node) => node.range(), - Self::Dict(node) => node.range(), - Self::Set(node) => node.range(), - Self::ListComp(node) => node.range(), - Self::SetComp(node) => node.range(), - Self::DictComp(node) => node.range(), - Self::GeneratorExp(node) => node.range(), - Self::Await(node) => node.range(), - Self::Yield(node) => node.range(), - Self::YieldFrom(node) => node.range(), - Self::Compare(node) => node.range(), - Self::Call(node) => node.range(), - Self::FormattedValue(node) => node.range(), - Self::JoinedStr(node) => node.range(), - Self::Constant(node) => node.range(), - Self::Attribute(node) => node.range(), - Self::Subscript(node) => node.range(), - Self::Starred(node) => node.range(), - Self::Name(node) => node.range(), - Self::List(node) => node.range(), - Self::Tuple(node) => node.range(), - Self::Slice(node) => node.range(), - } - } -} - -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::Comprehension { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::ExceptHandlerExceptHandler { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::ExceptHandler { - fn range(&self) -> TextRange { - match self { - Self::ExceptHandler(node) => node.range(), - } - } -} - -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::PythonArguments { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::Arg { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::Keyword { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::Alias { - fn range(&self) -> TextRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::WithItem { - fn range(&self) -> TextRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::MatchCase { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::PatternMatchValue { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::PatternMatchSingleton { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::PatternMatchSequence { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::PatternMatchMapping { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::PatternMatchClass { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::PatternMatchStar { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::PatternMatchAs { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::PatternMatchOr { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::Pattern { - fn range(&self) -> TextRange { - match self { - Self::MatchValue(node) => node.range(), - Self::MatchSingleton(node) => node.range(), - Self::MatchSequence(node) => node.range(), - Self::MatchMapping(node) => node.range(), - Self::MatchClass(node) => node.range(), - Self::MatchStar(node) => node.range(), - Self::MatchAs(node) => node.range(), - Self::MatchOr(node) => node.range(), - } - } -} - -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::TypeIgnoreTypeIgnore { - fn range(&self) -> TextRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::TypeIgnore { - fn range(&self) -> TextRange { - match self { - Self::TypeIgnore(node) => node.range(), - } - } -} - -impl Ranged for crate::generic::TypeParamTypeVar { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::TypeParamParamSpec { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::generic::TypeParamTypeVarTuple { - fn range(&self) -> TextRange { - self.range - } -} -impl Ranged for crate::TypeParam { - fn range(&self) -> TextRange { - match self { - Self::TypeVar(node) => node.range(), - Self::ParamSpec(node) => node.range(), - Self::TypeVarTuple(node) => node.range(), - } - } -} - -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::Arguments { - fn range(&self) -> TextRange { - self.range - } -} -#[cfg(feature = "all-nodes-with-ranges")] -impl Ranged for crate::generic::ArgWithDefault { - fn range(&self) -> TextRange { - self.range - } -} diff --git a/ast/src/gen/visitor.rs b/ast/src/gen/visitor.rs deleted file mode 100644 index d84e5423..00000000 --- a/ast/src/gen/visitor.rs +++ /dev/null @@ -1,865 +0,0 @@ -// File automatically generated by ast/asdl_rs.py. - -#[allow(unused_variables)] -pub trait Visitor { - fn visit_stmt(&mut self, node: Stmt) { - self.generic_visit_stmt(node) - } - fn generic_visit_stmt(&mut self, node: Stmt) { - match node { - Stmt::FunctionDef(data) => self.visit_stmt_function_def(data), - Stmt::AsyncFunctionDef(data) => self.visit_stmt_async_function_def(data), - Stmt::ClassDef(data) => self.visit_stmt_class_def(data), - Stmt::Return(data) => self.visit_stmt_return(data), - Stmt::Delete(data) => self.visit_stmt_delete(data), - Stmt::Assign(data) => self.visit_stmt_assign(data), - Stmt::TypeAlias(data) => self.visit_stmt_type_alias(data), - Stmt::AugAssign(data) => self.visit_stmt_aug_assign(data), - Stmt::AnnAssign(data) => self.visit_stmt_ann_assign(data), - Stmt::For(data) => self.visit_stmt_for(data), - Stmt::AsyncFor(data) => self.visit_stmt_async_for(data), - Stmt::While(data) => self.visit_stmt_while(data), - Stmt::If(data) => self.visit_stmt_if(data), - Stmt::With(data) => self.visit_stmt_with(data), - Stmt::AsyncWith(data) => self.visit_stmt_async_with(data), - Stmt::Match(data) => self.visit_stmt_match(data), - Stmt::Raise(data) => self.visit_stmt_raise(data), - Stmt::Try(data) => self.visit_stmt_try(data), - Stmt::TryStar(data) => self.visit_stmt_try_star(data), - Stmt::Assert(data) => self.visit_stmt_assert(data), - Stmt::Import(data) => self.visit_stmt_import(data), - Stmt::ImportFrom(data) => self.visit_stmt_import_from(data), - Stmt::Global(data) => self.visit_stmt_global(data), - Stmt::Nonlocal(data) => self.visit_stmt_nonlocal(data), - Stmt::Expr(data) => self.visit_stmt_expr(data), - Stmt::Pass(data) => self.visit_stmt_pass(data), - Stmt::Break(data) => self.visit_stmt_break(data), - Stmt::Continue(data) => self.visit_stmt_continue(data), - } - } - fn visit_stmt_function_def(&mut self, node: StmtFunctionDef) { - self.generic_visit_stmt_function_def(node) - } - fn generic_visit_stmt_function_def(&mut self, node: StmtFunctionDef) { - { - let value = node.args; - self.visit_arguments(*value); - } - for value in node.body { - self.visit_stmt(value); - } - for value in node.decorator_list { - self.visit_expr(value); - } - if let Some(value) = node.returns { - self.visit_expr(*value); - } - for value in node.type_params { - self.visit_type_param(value); - } - } - fn visit_stmt_async_function_def(&mut self, node: StmtAsyncFunctionDef) { - self.generic_visit_stmt_async_function_def(node) - } - fn generic_visit_stmt_async_function_def(&mut self, node: StmtAsyncFunctionDef) { - { - let value = node.args; - self.visit_arguments(*value); - } - for value in node.body { - self.visit_stmt(value); - } - for value in node.decorator_list { - self.visit_expr(value); - } - if let Some(value) = node.returns { - self.visit_expr(*value); - } - for value in node.type_params { - self.visit_type_param(value); - } - } - fn visit_stmt_class_def(&mut self, node: StmtClassDef) { - self.generic_visit_stmt_class_def(node) - } - fn generic_visit_stmt_class_def(&mut self, node: StmtClassDef) { - for value in node.bases { - self.visit_expr(value); - } - for value in node.keywords { - self.visit_keyword(value); - } - for value in node.body { - self.visit_stmt(value); - } - for value in node.decorator_list { - self.visit_expr(value); - } - for value in node.type_params { - self.visit_type_param(value); - } - } - fn visit_stmt_return(&mut self, node: StmtReturn) { - self.generic_visit_stmt_return(node) - } - fn generic_visit_stmt_return(&mut self, node: StmtReturn) { - if let Some(value) = node.value { - self.visit_expr(*value); - } - } - fn visit_stmt_delete(&mut self, node: StmtDelete) { - self.generic_visit_stmt_delete(node) - } - fn generic_visit_stmt_delete(&mut self, node: StmtDelete) { - for value in node.targets { - self.visit_expr(value); - } - } - fn visit_stmt_assign(&mut self, node: StmtAssign) { - self.generic_visit_stmt_assign(node) - } - fn generic_visit_stmt_assign(&mut self, node: StmtAssign) { - for value in node.targets { - self.visit_expr(value); - } - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_stmt_type_alias(&mut self, node: StmtTypeAlias) { - self.generic_visit_stmt_type_alias(node) - } - fn generic_visit_stmt_type_alias(&mut self, node: StmtTypeAlias) { - { - let value = node.name; - self.visit_expr(*value); - } - for value in node.type_params { - self.visit_type_param(value); - } - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_stmt_aug_assign(&mut self, node: StmtAugAssign) { - self.generic_visit_stmt_aug_assign(node) - } - fn generic_visit_stmt_aug_assign(&mut self, node: StmtAugAssign) { - { - let value = node.target; - self.visit_expr(*value); - } - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_stmt_ann_assign(&mut self, node: StmtAnnAssign) { - self.generic_visit_stmt_ann_assign(node) - } - fn generic_visit_stmt_ann_assign(&mut self, node: StmtAnnAssign) { - { - let value = node.target; - self.visit_expr(*value); - } - { - let value = node.annotation; - self.visit_expr(*value); - } - if let Some(value) = node.value { - self.visit_expr(*value); - } - } - fn visit_stmt_for(&mut self, node: StmtFor) { - self.generic_visit_stmt_for(node) - } - fn generic_visit_stmt_for(&mut self, node: StmtFor) { - { - let value = node.target; - self.visit_expr(*value); - } - { - let value = node.iter; - self.visit_expr(*value); - } - for value in node.body { - self.visit_stmt(value); - } - for value in node.orelse { - self.visit_stmt(value); - } - } - fn visit_stmt_async_for(&mut self, node: StmtAsyncFor) { - self.generic_visit_stmt_async_for(node) - } - fn generic_visit_stmt_async_for(&mut self, node: StmtAsyncFor) { - { - let value = node.target; - self.visit_expr(*value); - } - { - let value = node.iter; - self.visit_expr(*value); - } - for value in node.body { - self.visit_stmt(value); - } - for value in node.orelse { - self.visit_stmt(value); - } - } - fn visit_stmt_while(&mut self, node: StmtWhile) { - self.generic_visit_stmt_while(node) - } - fn generic_visit_stmt_while(&mut self, node: StmtWhile) { - { - let value = node.test; - self.visit_expr(*value); - } - for value in node.body { - self.visit_stmt(value); - } - for value in node.orelse { - self.visit_stmt(value); - } - } - fn visit_stmt_if(&mut self, node: StmtIf) { - self.generic_visit_stmt_if(node) - } - fn generic_visit_stmt_if(&mut self, node: StmtIf) { - { - let value = node.test; - self.visit_expr(*value); - } - for value in node.body { - self.visit_stmt(value); - } - for value in node.orelse { - self.visit_stmt(value); - } - } - fn visit_stmt_with(&mut self, node: StmtWith) { - self.generic_visit_stmt_with(node) - } - fn generic_visit_stmt_with(&mut self, node: StmtWith) { - for value in node.items { - self.visit_withitem(value); - } - for value in node.body { - self.visit_stmt(value); - } - } - fn visit_stmt_async_with(&mut self, node: StmtAsyncWith) { - self.generic_visit_stmt_async_with(node) - } - fn generic_visit_stmt_async_with(&mut self, node: StmtAsyncWith) { - for value in node.items { - self.visit_withitem(value); - } - for value in node.body { - self.visit_stmt(value); - } - } - fn visit_stmt_match(&mut self, node: StmtMatch) { - self.generic_visit_stmt_match(node) - } - fn generic_visit_stmt_match(&mut self, node: StmtMatch) { - { - let value = node.subject; - self.visit_expr(*value); - } - for value in node.cases { - self.visit_match_case(value); - } - } - fn visit_stmt_raise(&mut self, node: StmtRaise) { - self.generic_visit_stmt_raise(node) - } - fn generic_visit_stmt_raise(&mut self, node: StmtRaise) { - if let Some(value) = node.exc { - self.visit_expr(*value); - } - if let Some(value) = node.cause { - self.visit_expr(*value); - } - } - fn visit_stmt_try(&mut self, node: StmtTry) { - self.generic_visit_stmt_try(node) - } - fn generic_visit_stmt_try(&mut self, node: StmtTry) { - for value in node.body { - self.visit_stmt(value); - } - for value in node.handlers { - self.visit_excepthandler(value); - } - for value in node.orelse { - self.visit_stmt(value); - } - for value in node.finalbody { - self.visit_stmt(value); - } - } - fn visit_stmt_try_star(&mut self, node: StmtTryStar) { - self.generic_visit_stmt_try_star(node) - } - fn generic_visit_stmt_try_star(&mut self, node: StmtTryStar) { - for value in node.body { - self.visit_stmt(value); - } - for value in node.handlers { - self.visit_excepthandler(value); - } - for value in node.orelse { - self.visit_stmt(value); - } - for value in node.finalbody { - self.visit_stmt(value); - } - } - fn visit_stmt_assert(&mut self, node: StmtAssert) { - self.generic_visit_stmt_assert(node) - } - fn generic_visit_stmt_assert(&mut self, node: StmtAssert) { - { - let value = node.test; - self.visit_expr(*value); - } - if let Some(value) = node.msg { - self.visit_expr(*value); - } - } - fn visit_stmt_import(&mut self, node: StmtImport) { - self.generic_visit_stmt_import(node) - } - fn generic_visit_stmt_import(&mut self, node: StmtImport) { - for value in node.names { - self.visit_alias(value); - } - } - fn visit_stmt_import_from(&mut self, node: StmtImportFrom) { - self.generic_visit_stmt_import_from(node) - } - fn generic_visit_stmt_import_from(&mut self, node: StmtImportFrom) { - for value in node.names { - self.visit_alias(value); - } - } - fn visit_stmt_global(&mut self, node: StmtGlobal) { - self.generic_visit_stmt_global(node) - } - fn generic_visit_stmt_global(&mut self, node: StmtGlobal) {} - fn visit_stmt_nonlocal(&mut self, node: StmtNonlocal) { - self.generic_visit_stmt_nonlocal(node) - } - fn generic_visit_stmt_nonlocal(&mut self, node: StmtNonlocal) {} - fn visit_stmt_expr(&mut self, node: StmtExpr) { - self.generic_visit_stmt_expr(node) - } - fn generic_visit_stmt_expr(&mut self, node: StmtExpr) { - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_stmt_pass(&mut self, node: StmtPass) {} - fn visit_stmt_break(&mut self, node: StmtBreak) {} - fn visit_stmt_continue(&mut self, node: StmtContinue) {} - fn visit_expr(&mut self, node: Expr) { - self.generic_visit_expr(node) - } - fn generic_visit_expr(&mut self, node: Expr) { - match node { - Expr::BoolOp(data) => self.visit_expr_bool_op(data), - Expr::NamedExpr(data) => self.visit_expr_named_expr(data), - Expr::BinOp(data) => self.visit_expr_bin_op(data), - Expr::UnaryOp(data) => self.visit_expr_unary_op(data), - Expr::Lambda(data) => self.visit_expr_lambda(data), - Expr::IfExp(data) => self.visit_expr_if_exp(data), - Expr::Dict(data) => self.visit_expr_dict(data), - Expr::Set(data) => self.visit_expr_set(data), - Expr::ListComp(data) => self.visit_expr_list_comp(data), - Expr::SetComp(data) => self.visit_expr_set_comp(data), - Expr::DictComp(data) => self.visit_expr_dict_comp(data), - Expr::GeneratorExp(data) => self.visit_expr_generator_exp(data), - Expr::Await(data) => self.visit_expr_await(data), - Expr::Yield(data) => self.visit_expr_yield(data), - Expr::YieldFrom(data) => self.visit_expr_yield_from(data), - Expr::Compare(data) => self.visit_expr_compare(data), - Expr::Call(data) => self.visit_expr_call(data), - Expr::FormattedValue(data) => self.visit_expr_formatted_value(data), - Expr::JoinedStr(data) => self.visit_expr_joined_str(data), - Expr::Constant(data) => self.visit_expr_constant(data), - Expr::Attribute(data) => self.visit_expr_attribute(data), - Expr::Subscript(data) => self.visit_expr_subscript(data), - Expr::Starred(data) => self.visit_expr_starred(data), - Expr::Name(data) => self.visit_expr_name(data), - Expr::List(data) => self.visit_expr_list(data), - Expr::Tuple(data) => self.visit_expr_tuple(data), - Expr::Slice(data) => self.visit_expr_slice(data), - } - } - fn visit_expr_bool_op(&mut self, node: ExprBoolOp) { - self.generic_visit_expr_bool_op(node) - } - fn generic_visit_expr_bool_op(&mut self, node: ExprBoolOp) { - for value in node.values { - self.visit_expr(value); - } - } - fn visit_expr_named_expr(&mut self, node: ExprNamedExpr) { - self.generic_visit_expr_named_expr(node) - } - fn generic_visit_expr_named_expr(&mut self, node: ExprNamedExpr) { - { - let value = node.target; - self.visit_expr(*value); - } - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_expr_bin_op(&mut self, node: ExprBinOp) { - self.generic_visit_expr_bin_op(node) - } - fn generic_visit_expr_bin_op(&mut self, node: ExprBinOp) { - { - let value = node.left; - self.visit_expr(*value); - } - { - let value = node.right; - self.visit_expr(*value); - } - } - fn visit_expr_unary_op(&mut self, node: ExprUnaryOp) { - self.generic_visit_expr_unary_op(node) - } - fn generic_visit_expr_unary_op(&mut self, node: ExprUnaryOp) { - { - let value = node.operand; - self.visit_expr(*value); - } - } - fn visit_expr_lambda(&mut self, node: ExprLambda) { - self.generic_visit_expr_lambda(node) - } - fn generic_visit_expr_lambda(&mut self, node: ExprLambda) { - { - let value = node.args; - self.visit_arguments(*value); - } - { - let value = node.body; - self.visit_expr(*value); - } - } - fn visit_expr_if_exp(&mut self, node: ExprIfExp) { - self.generic_visit_expr_if_exp(node) - } - fn generic_visit_expr_if_exp(&mut self, node: ExprIfExp) { - { - let value = node.test; - self.visit_expr(*value); - } - { - let value = node.body; - self.visit_expr(*value); - } - { - let value = node.orelse; - self.visit_expr(*value); - } - } - fn visit_expr_dict(&mut self, node: ExprDict) { - self.generic_visit_expr_dict(node) - } - fn generic_visit_expr_dict(&mut self, node: ExprDict) { - for value in node.keys.into_iter().flatten() { - self.visit_expr(value); - } - for value in node.values { - self.visit_expr(value); - } - } - fn visit_expr_set(&mut self, node: ExprSet) { - self.generic_visit_expr_set(node) - } - fn generic_visit_expr_set(&mut self, node: ExprSet) { - for value in node.elts { - self.visit_expr(value); - } - } - fn visit_expr_list_comp(&mut self, node: ExprListComp) { - self.generic_visit_expr_list_comp(node) - } - fn generic_visit_expr_list_comp(&mut self, node: ExprListComp) { - { - let value = node.elt; - self.visit_expr(*value); - } - for value in node.generators { - self.visit_comprehension(value); - } - } - fn visit_expr_set_comp(&mut self, node: ExprSetComp) { - self.generic_visit_expr_set_comp(node) - } - fn generic_visit_expr_set_comp(&mut self, node: ExprSetComp) { - { - let value = node.elt; - self.visit_expr(*value); - } - for value in node.generators { - self.visit_comprehension(value); - } - } - fn visit_expr_dict_comp(&mut self, node: ExprDictComp) { - self.generic_visit_expr_dict_comp(node) - } - fn generic_visit_expr_dict_comp(&mut self, node: ExprDictComp) { - { - let value = node.key; - self.visit_expr(*value); - } - { - let value = node.value; - self.visit_expr(*value); - } - for value in node.generators { - self.visit_comprehension(value); - } - } - fn visit_expr_generator_exp(&mut self, node: ExprGeneratorExp) { - self.generic_visit_expr_generator_exp(node) - } - fn generic_visit_expr_generator_exp(&mut self, node: ExprGeneratorExp) { - { - let value = node.elt; - self.visit_expr(*value); - } - for value in node.generators { - self.visit_comprehension(value); - } - } - fn visit_expr_await(&mut self, node: ExprAwait) { - self.generic_visit_expr_await(node) - } - fn generic_visit_expr_await(&mut self, node: ExprAwait) { - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_expr_yield(&mut self, node: ExprYield) { - self.generic_visit_expr_yield(node) - } - fn generic_visit_expr_yield(&mut self, node: ExprYield) { - if let Some(value) = node.value { - self.visit_expr(*value); - } - } - fn visit_expr_yield_from(&mut self, node: ExprYieldFrom) { - self.generic_visit_expr_yield_from(node) - } - fn generic_visit_expr_yield_from(&mut self, node: ExprYieldFrom) { - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_expr_compare(&mut self, node: ExprCompare) { - self.generic_visit_expr_compare(node) - } - fn generic_visit_expr_compare(&mut self, node: ExprCompare) { - { - let value = node.left; - self.visit_expr(*value); - } - for value in node.comparators { - self.visit_expr(value); - } - } - fn visit_expr_call(&mut self, node: ExprCall) { - self.generic_visit_expr_call(node) - } - fn generic_visit_expr_call(&mut self, node: ExprCall) { - { - let value = node.func; - self.visit_expr(*value); - } - for value in node.args { - self.visit_expr(value); - } - for value in node.keywords { - self.visit_keyword(value); - } - } - fn visit_expr_formatted_value(&mut self, node: ExprFormattedValue) { - self.generic_visit_expr_formatted_value(node) - } - fn generic_visit_expr_formatted_value(&mut self, node: ExprFormattedValue) { - { - let value = node.value; - self.visit_expr(*value); - } - if let Some(value) = node.format_spec { - self.visit_expr(*value); - } - } - fn visit_expr_joined_str(&mut self, node: ExprJoinedStr) { - self.generic_visit_expr_joined_str(node) - } - fn generic_visit_expr_joined_str(&mut self, node: ExprJoinedStr) { - for value in node.values { - self.visit_expr(value); - } - } - fn visit_expr_constant(&mut self, node: ExprConstant) { - self.generic_visit_expr_constant(node) - } - fn generic_visit_expr_constant(&mut self, node: ExprConstant) {} - fn visit_expr_attribute(&mut self, node: ExprAttribute) { - self.generic_visit_expr_attribute(node) - } - fn generic_visit_expr_attribute(&mut self, node: ExprAttribute) { - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_expr_subscript(&mut self, node: ExprSubscript) { - self.generic_visit_expr_subscript(node) - } - fn generic_visit_expr_subscript(&mut self, node: ExprSubscript) { - { - let value = node.value; - self.visit_expr(*value); - } - { - let value = node.slice; - self.visit_expr(*value); - } - } - fn visit_expr_starred(&mut self, node: ExprStarred) { - self.generic_visit_expr_starred(node) - } - fn generic_visit_expr_starred(&mut self, node: ExprStarred) { - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_expr_name(&mut self, node: ExprName) { - self.generic_visit_expr_name(node) - } - fn generic_visit_expr_name(&mut self, node: ExprName) {} - fn visit_expr_list(&mut self, node: ExprList) { - self.generic_visit_expr_list(node) - } - fn generic_visit_expr_list(&mut self, node: ExprList) { - for value in node.elts { - self.visit_expr(value); - } - } - fn visit_expr_tuple(&mut self, node: ExprTuple) { - self.generic_visit_expr_tuple(node) - } - fn generic_visit_expr_tuple(&mut self, node: ExprTuple) { - for value in node.elts { - self.visit_expr(value); - } - } - fn visit_expr_slice(&mut self, node: ExprSlice) { - self.generic_visit_expr_slice(node) - } - fn generic_visit_expr_slice(&mut self, node: ExprSlice) { - if let Some(value) = node.lower { - self.visit_expr(*value); - } - if let Some(value) = node.upper { - self.visit_expr(*value); - } - if let Some(value) = node.step { - self.visit_expr(*value); - } - } - fn visit_expr_context(&mut self, node: ExprContext) { - self.generic_visit_expr_context(node) - } - fn generic_visit_expr_context(&mut self, node: ExprContext) {} - fn visit_boolop(&mut self, node: BoolOp) { - self.generic_visit_boolop(node) - } - fn generic_visit_boolop(&mut self, node: BoolOp) {} - fn visit_operator(&mut self, node: Operator) { - self.generic_visit_operator(node) - } - fn generic_visit_operator(&mut self, node: Operator) {} - fn visit_unaryop(&mut self, node: UnaryOp) { - self.generic_visit_unaryop(node) - } - fn generic_visit_unaryop(&mut self, node: UnaryOp) {} - fn visit_cmpop(&mut self, node: CmpOp) { - self.generic_visit_cmpop(node) - } - fn generic_visit_cmpop(&mut self, node: CmpOp) {} - fn visit_comprehension(&mut self, node: Comprehension) { - self.generic_visit_comprehension(node) - } - fn generic_visit_comprehension(&mut self, node: Comprehension) {} - fn visit_excepthandler(&mut self, node: ExceptHandler) { - self.generic_visit_excepthandler(node) - } - fn generic_visit_excepthandler(&mut self, node: ExceptHandler) { - match node { - ExceptHandler::ExceptHandler(data) => self.visit_excepthandler_except_handler(data), - } - } - fn visit_excepthandler_except_handler(&mut self, node: ExceptHandlerExceptHandler) { - self.generic_visit_excepthandler_except_handler(node) - } - fn generic_visit_excepthandler_except_handler(&mut self, node: ExceptHandlerExceptHandler) { - if let Some(value) = node.type_ { - self.visit_expr(*value); - } - for value in node.body { - self.visit_stmt(value); - } - } - fn visit_arguments(&mut self, node: Arguments) { - self.generic_visit_arguments(node) - } - fn generic_visit_arguments(&mut self, node: Arguments) {} - fn visit_arg(&mut self, node: Arg) { - self.generic_visit_arg(node) - } - fn generic_visit_arg(&mut self, node: Arg) {} - fn visit_keyword(&mut self, node: Keyword) { - self.generic_visit_keyword(node) - } - fn generic_visit_keyword(&mut self, node: Keyword) {} - fn visit_alias(&mut self, node: Alias) { - self.generic_visit_alias(node) - } - fn generic_visit_alias(&mut self, node: Alias) {} - fn visit_withitem(&mut self, node: WithItem) { - self.generic_visit_withitem(node) - } - fn generic_visit_withitem(&mut self, node: WithItem) {} - fn visit_match_case(&mut self, node: MatchCase) { - self.generic_visit_match_case(node) - } - fn generic_visit_match_case(&mut self, node: MatchCase) {} - fn visit_pattern(&mut self, node: Pattern) { - self.generic_visit_pattern(node) - } - fn generic_visit_pattern(&mut self, node: Pattern) { - match node { - Pattern::MatchValue(data) => self.visit_pattern_match_value(data), - Pattern::MatchSingleton(data) => self.visit_pattern_match_singleton(data), - Pattern::MatchSequence(data) => self.visit_pattern_match_sequence(data), - Pattern::MatchMapping(data) => self.visit_pattern_match_mapping(data), - Pattern::MatchClass(data) => self.visit_pattern_match_class(data), - Pattern::MatchStar(data) => self.visit_pattern_match_star(data), - Pattern::MatchAs(data) => self.visit_pattern_match_as(data), - Pattern::MatchOr(data) => self.visit_pattern_match_or(data), - } - } - fn visit_pattern_match_value(&mut self, node: PatternMatchValue) { - self.generic_visit_pattern_match_value(node) - } - fn generic_visit_pattern_match_value(&mut self, node: PatternMatchValue) { - { - let value = node.value; - self.visit_expr(*value); - } - } - fn visit_pattern_match_singleton(&mut self, node: PatternMatchSingleton) { - self.generic_visit_pattern_match_singleton(node) - } - fn generic_visit_pattern_match_singleton(&mut self, node: PatternMatchSingleton) {} - fn visit_pattern_match_sequence(&mut self, node: PatternMatchSequence) { - self.generic_visit_pattern_match_sequence(node) - } - fn generic_visit_pattern_match_sequence(&mut self, node: PatternMatchSequence) { - for value in node.patterns { - self.visit_pattern(value); - } - } - fn visit_pattern_match_mapping(&mut self, node: PatternMatchMapping) { - self.generic_visit_pattern_match_mapping(node) - } - fn generic_visit_pattern_match_mapping(&mut self, node: PatternMatchMapping) { - for value in node.keys { - self.visit_expr(value); - } - for value in node.patterns { - self.visit_pattern(value); - } - } - fn visit_pattern_match_class(&mut self, node: PatternMatchClass) { - self.generic_visit_pattern_match_class(node) - } - fn generic_visit_pattern_match_class(&mut self, node: PatternMatchClass) { - { - let value = node.cls; - self.visit_expr(*value); - } - for value in node.patterns { - self.visit_pattern(value); - } - for value in node.kwd_patterns { - self.visit_pattern(value); - } - } - fn visit_pattern_match_star(&mut self, node: PatternMatchStar) { - self.generic_visit_pattern_match_star(node) - } - fn generic_visit_pattern_match_star(&mut self, node: PatternMatchStar) {} - fn visit_pattern_match_as(&mut self, node: PatternMatchAs) { - self.generic_visit_pattern_match_as(node) - } - fn generic_visit_pattern_match_as(&mut self, node: PatternMatchAs) { - if let Some(value) = node.pattern { - self.visit_pattern(*value); - } - } - fn visit_pattern_match_or(&mut self, node: PatternMatchOr) { - self.generic_visit_pattern_match_or(node) - } - fn generic_visit_pattern_match_or(&mut self, node: PatternMatchOr) { - for value in node.patterns { - self.visit_pattern(value); - } - } - fn visit_type_param(&mut self, node: TypeParam) { - self.generic_visit_type_param(node) - } - fn generic_visit_type_param(&mut self, node: TypeParam) { - match node { - TypeParam::TypeVar(data) => self.visit_type_param_type_var(data), - TypeParam::ParamSpec(data) => self.visit_type_param_param_spec(data), - TypeParam::TypeVarTuple(data) => self.visit_type_param_type_var_tuple(data), - } - } - fn visit_type_param_type_var(&mut self, node: TypeParamTypeVar) { - self.generic_visit_type_param_type_var(node) - } - fn generic_visit_type_param_type_var(&mut self, node: TypeParamTypeVar) { - if let Some(value) = node.bound { - self.visit_expr(*value); - } - } - fn visit_type_param_param_spec(&mut self, node: TypeParamParamSpec) { - self.generic_visit_type_param_param_spec(node) - } - fn generic_visit_type_param_param_spec(&mut self, node: TypeParamParamSpec) {} - fn visit_type_param_type_var_tuple(&mut self, node: TypeParamTypeVarTuple) { - self.generic_visit_type_param_type_var_tuple(node) - } - fn generic_visit_type_param_type_var_tuple(&mut self, node: TypeParamTypeVarTuple) {} -} diff --git a/ast/src/generic.rs b/ast/src/generic.rs deleted file mode 100644 index fe0875b9..00000000 --- a/ast/src/generic.rs +++ /dev/null @@ -1,327 +0,0 @@ -#![allow(clippy::derive_partial_eq_without_eq)] -pub use crate::{builtin::*, text_size::TextSize, ConversionFlag, Node}; -use std::fmt::{Debug, Display, Formatter}; -use std::marker::PhantomData; - -pub type Suite = Vec>; - -#[cfg(feature = "all-nodes-with-ranges")] -pub type OptionalRange = R; - -#[cfg(not(feature = "all-nodes-with-ranges"))] -pub type OptionalRange = EmptyRange; - -#[cfg(not(feature = "all-nodes-with-ranges"))] -impl From for OptionalRange { - fn from(_: R) -> Self { - Self { - phantom: PhantomData, - } - } -} - -#[derive(Eq, PartialEq, Hash, Copy, Clone)] -pub struct EmptyRange { - phantom: PhantomData, -} - -impl EmptyRange { - #[inline(always)] - pub fn new(_start: TextSize, _end: TextSize) -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl Display for EmptyRange { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("()") - } -} - -impl Debug for EmptyRange { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Display::fmt(self, f) - } -} - -impl Default for EmptyRange { - fn default() -> Self { - EmptyRange { - phantom: PhantomData, - } - } -} - -impl CmpOp { - pub fn as_str(&self) -> &'static str { - match self { - CmpOp::Eq => "==", - CmpOp::NotEq => "!=", - CmpOp::Lt => "<", - CmpOp::LtE => "<=", - CmpOp::Gt => ">", - CmpOp::GtE => ">=", - CmpOp::Is => "is", - CmpOp::IsNot => "is not", - CmpOp::In => "in", - CmpOp::NotIn => "not in", - } - } -} - -impl Arguments { - pub fn empty(range: OptionalRange) -> Self { - Self { - range, - posonlyargs: Vec::new(), - args: Vec::new(), - vararg: None, - kwonlyargs: Vec::new(), - kwarg: None, - } - } -} - -#[allow(clippy::borrowed_box)] // local utility -fn clone_boxed_expr(expr: &Box>) -> Box> { - let expr: &Expr<_> = expr.as_ref(); - Box::new(expr.clone()) -} - -impl ArgWithDefault { - pub fn from_arg(def: Arg, default: Option>) -> Self - where - R: Clone, - { - let range = { - if cfg!(feature = "all-nodes-with-ranges") { - todo!("range recovery is not implemented yet") // def.range.start()..default.range.end() - } else { - #[allow(clippy::useless_conversion)] // false positive by cfg - OptionalRange::from(def.range.clone()) - } - }; - Self { - range, - def, - default: default.map(Box::new), - } - } - - pub fn as_arg(&self) -> &Arg { - &self.def - } - - pub fn to_arg(&self) -> (Arg, Option>>) - where - R: Clone, - { - let ArgWithDefault { - range: _, - def, - default, - } = self; - (def.clone(), default.as_ref().map(clone_boxed_expr)) - } - pub fn into_arg(self) -> (Arg, Option>>) { - let ArgWithDefault { - range: _, - def, - default, - } = self; - (def, default) - } -} - -impl Arguments { - pub fn defaults(&self) -> impl std::iter::Iterator> { - self.posonlyargs - .iter() - .chain(self.args.iter()) - .filter_map(|arg| arg.default.as_ref().map(|e| e.as_ref())) - } - - #[allow(clippy::type_complexity)] - pub fn split_kwonlyargs(&self) -> (Vec<&Arg>, Vec<(&Arg, &Expr)>) { - let mut args = Vec::new(); - let mut with_defaults = Vec::new(); - for arg in self.kwonlyargs.iter() { - if let Some(ref default) = arg.default { - with_defaults.push((arg.as_arg(), &**default)); - } else { - args.push(arg.as_arg()); - } - } - (args, with_defaults) - } - - pub fn to_python_arguments(&self) -> PythonArguments - where - R: Clone, - { - let Arguments { - range, - posonlyargs, - args, - vararg, - kwonlyargs, - kwarg, - } = self; - - let mut pos_only = Vec::with_capacity(posonlyargs.len()); - let mut pos_args = Vec::with_capacity(args.len()); - let mut defaults = Vec::new(); - for arg in posonlyargs { - let (arg, default) = arg.to_arg(); - if let Some(default) = default { - defaults.push(*default); - } - pos_only.push(arg); - } - for arg in args { - let (arg, default) = arg.to_arg(); - if let Some(default) = default { - defaults.push(*default); - } - pos_args.push(arg); - } - - let mut kw_only = Vec::with_capacity(kwonlyargs.len()); - let mut kw_defaults = Vec::new(); - for arg in kwonlyargs { - let (arg, default) = arg.to_arg(); - if let Some(default) = default { - kw_defaults.push(*default); - } - kw_only.push(arg); - } - - PythonArguments { - range: range.clone(), - posonlyargs: pos_only, - args: pos_args, - defaults, - vararg: vararg.clone(), - kwonlyargs: kw_only, - kw_defaults, - kwarg: kwarg.clone(), - } - } - - pub fn into_python_arguments(self) -> PythonArguments { - let Arguments { - range, - posonlyargs, - args, - vararg, - kwonlyargs, - kwarg, - } = self; - - let mut pos_only = Vec::with_capacity(posonlyargs.len()); - let mut pos_args = Vec::with_capacity(args.len()); - let mut defaults = Vec::new(); - for arg in posonlyargs { - let (arg, default) = arg.into_arg(); - if let Some(default) = default { - defaults.push(*default); - } - pos_only.push(arg); - } - for arg in args { - let (arg, default) = arg.into_arg(); - if let Some(default) = default { - defaults.push(*default); - } - pos_args.push(arg); - } - - let mut kw_only = Vec::with_capacity(kwonlyargs.len()); - let mut kw_defaults = Vec::new(); - for arg in kwonlyargs { - let (arg, default) = arg.into_arg(); - if let Some(default) = default { - kw_defaults.push(*default); - } - kw_only.push(arg); - } - - PythonArguments { - range, - posonlyargs: pos_only, - args: pos_args, - defaults, - vararg, - kwonlyargs: kw_only, - kw_defaults, - kwarg, - } - } -} - -impl PythonArguments { - pub fn into_arguments(self) -> Arguments - where - R: Clone, - { - let PythonArguments { - range, - posonlyargs, - args, - defaults, - vararg, - kwonlyargs, - kw_defaults, - kwarg, - } = self; - - let mut pos_only = Vec::with_capacity(posonlyargs.len()); - let mut pos_args = Vec::with_capacity(args.len()); - let args_len = posonlyargs.len() + args.len(); - // not optimal - let mut defaults: Vec<_> = std::iter::repeat_with(|| None) - .take(args_len - defaults.len()) - .chain(defaults.into_iter().map(Some)) - .collect(); - debug_assert_eq!(args_len, defaults.len()); - - for (arg, default) in std::iter::zip(args, defaults.drain(posonlyargs.len()..)) { - let arg = ArgWithDefault::from_arg(arg, default); - pos_args.push(arg); - } - - for (arg, default) in std::iter::zip(posonlyargs, defaults.drain(..)) { - let arg = ArgWithDefault::from_arg(arg, default); - pos_only.push(arg); - } - - let mut kw_only = Vec::with_capacity(kwonlyargs.len()); - let kw_defaults: Vec<_> = std::iter::repeat_with(|| None) - .take(kw_only.len().saturating_sub(kw_defaults.len())) - .chain(kw_defaults.into_iter().map(Some)) - .collect(); - for (arg, default) in std::iter::zip(kwonlyargs, kw_defaults) { - let arg = ArgWithDefault::from_arg(arg, default); - kw_only.push(arg); - } - - Arguments { - range, - posonlyargs: pos_only, - args: pos_args, - vararg, - kwonlyargs: kw_only, - kwarg, - } - } -} - -impl From> for PythonArguments { - fn from(arguments: Arguments) -> Self { - arguments.into_python_arguments() - } -} - -include!("gen/generic.rs"); diff --git a/ast/src/impls.rs b/ast/src/impls.rs index 8c788593..a9302887 100644 --- a/ast/src/impls.rs +++ b/ast/src/impls.rs @@ -1,17 +1,19 @@ -use crate::{Constant, Expr}; +use crate::{Constant, ExprKind}; -impl Expr { +impl ExprKind { /// Returns a short name for the node suitable for use in error messages. - pub fn python_name(&self) -> &'static str { + pub fn 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::Constant(crate::ExprConstant { value, .. }) => match value { + ExprKind::BoolOp { .. } | ExprKind::BinOp { .. } | ExprKind::UnaryOp { .. } => { + "operator" + } + ExprKind::Subscript { .. } => "subscript", + ExprKind::Await { .. } => "await expression", + ExprKind::Yield { .. } | ExprKind::YieldFrom { .. } => "yield expression", + ExprKind::Compare { .. } => "comparison", + ExprKind::Attribute { .. } => "attribute", + ExprKind::Call { .. } => "function call", + ExprKind::Constant { value, .. } => match value { Constant::Str(_) | Constant::Int(_) | Constant::Float(_) @@ -28,38 +30,31 @@ impl Expr { Constant::None => "None", Constant::Ellipsis => "ellipsis", }, - Expr::List { .. } => "list", - Expr::Tuple { .. } => "tuple", - Expr::Dict { .. } => "dict display", - Expr::Set { .. } => "set display", - Expr::ListComp { .. } => "list comprehension", - Expr::DictComp { .. } => "dict comprehension", - Expr::SetComp { .. } => "set comprehension", - Expr::GeneratorExp { .. } => "generator expression", - Expr::Starred { .. } => "starred", - Expr::Slice { .. } => "slice", - Expr::JoinedStr(crate::ExprJoinedStr { values, .. }) => { - if values.iter().any(|e| e.is_joined_str_expr()) { + ExprKind::List { .. } => "list", + ExprKind::Tuple { .. } => "tuple", + ExprKind::Dict { .. } => "dict display", + ExprKind::Set { .. } => "set display", + ExprKind::ListComp { .. } => "list comprehension", + ExprKind::DictComp { .. } => "dict comprehension", + ExprKind::SetComp { .. } => "set comprehension", + ExprKind::GeneratorExp { .. } => "generator expression", + ExprKind::Starred { .. } => "starred", + ExprKind::Slice { .. } => "slice", + ExprKind::JoinedStr { values } => { + if values + .iter() + .any(|e| matches!(e.node, ExprKind::JoinedStr { .. })) + { "f-string expression" } else { "literal" } } - Expr::FormattedValue { .. } => "f-string expression", - Expr::Name { .. } => "name", - Expr::Lambda { .. } => "lambda", - Expr::IfExp { .. } => "conditional expression", - Expr::NamedExpr { .. } => "named expression", + ExprKind::FormattedValue { .. } => "f-string expression", + ExprKind::Name { .. } => "name", + ExprKind::Lambda { .. } => "lambda", + ExprKind::IfExp { .. } => "conditional expression", + ExprKind::NamedExpr { .. } => "named expression", } } } - -// TODO: make this a #[test] to avoid eq comparison -// #[cfg(target_arch = "x86_64")] -// static_assertions::assert_eq_size!(crate::Expr, [u8; 72]); -// #[cfg(target_arch = "x86_64")] -// static_assertions::assert_eq_size!(crate::Stmt, [u8; 160]); -// #[cfg(target_arch = "x86_64")] -// static_assertions::assert_eq_size!(crate::Pattern, [u8; 96]); -// #[cfg(target_arch = "x86_64")] -// static_assertions::assert_eq_size!(crate::ExceptHandler, [u8; 64]); diff --git a/ast/src/lib.rs b/ast/src/lib.rs index 143fac1c..d668bede 100644 --- a/ast/src/lib.rs +++ b/ast/src/lib.rs @@ -1,65 +1,12 @@ -//! Python AST node definitions and utilities. -//! -//! AST nodes are very similary defined like [Python AST](https://docs.python.org/3/library/ast.html). -//! But a few exceptions exist due to parser optimization. -//! They can be transformed to matching Python-styled AST in reasonable cost. -//! -//! [PythonArguments] is replaced by [Arguments]. The new [Arguments] type representation uses a new type -//! [ArgWithDefault] to represent arguments with default values. See each type documentation for more details. -//! -//! A few top-level sum types are renamed to human friendly names. -//! [CmpOp] refers `cmpop` -//! [UnaryOp] refers `unaryop` -//! [BoolOp] refers `boolop` -//! [WithItem] refers `withitem` -//! [ExceptHandler] refers `excepthandler` -//! - -mod builtin; -mod generic; +mod ast_gen; +mod constant; +#[cfg(feature = "fold")] +mod fold_helpers; mod impls; -mod ranged; #[cfg(feature = "unparse")] -pub mod unparse; - -#[cfg(feature = "malachite-bigint")] -pub use malachite_bigint as bigint; -#[cfg(feature = "num-bigint")] -pub use num_bigint as bigint; - -pub use builtin::*; -pub use generic::*; -pub use ranged::Ranged; -pub use rustpython_parser_core::{text_size, ConversionFlag}; - -pub trait Node { - const NAME: &'static str; - const FIELD_NAMES: &'static [&'static str]; -} - -#[cfg(feature = "fold")] -pub mod fold; -#[cfg(feature = "fold")] -pub use fold::Fold; - -#[cfg(feature = "visitor")] -mod visitor { - use super::generic::*; - - include!("gen/visitor.rs"); -} - -#[cfg(feature = "location")] -pub mod located; -#[cfg(feature = "location")] -mod source_locator; -#[cfg(feature = "location")] -pub use rustpython_parser_core::source_code; +mod unparse; -#[cfg(feature = "visitor")] -pub use visitor::Visitor; +pub use ast_gen::*; +pub use rustpython_compiler_core::Location; -#[cfg(feature = "constant-optimization")] -mod optimizer; -#[cfg(feature = "constant-optimization")] -pub use optimizer::ConstantOptimizer; +pub type Suite = Vec>; diff --git a/ast/src/located.rs b/ast/src/located.rs deleted file mode 100644 index bf252617..00000000 --- a/ast/src/located.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![allow(clippy::derive_partial_eq_without_eq)] -use crate::source_code::{SourceLocation, SourceRange}; - -pub trait Located { - fn range(&self) -> SourceRange; - - fn location(&self) -> SourceLocation { - self.range().start - } - - fn end_location(&self) -> Option { - self.range().end - } -} - -pub trait LocatedMut: Located { - fn range_mut(&mut self) -> &mut SourceRange; -} - -pub type Suite = Vec; - -pub use crate::builtin::*; -include!("gen/located.rs"); diff --git a/ast/src/optimizer.rs b/ast/src/optimizer.rs deleted file mode 100644 index 42a6dddc..00000000 --- a/ast/src/optimizer.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::builtin::Constant; - -#[non_exhaustive] -#[derive(Default)] -pub struct ConstantOptimizer {} - -impl ConstantOptimizer { - #[inline] - pub fn new() -> Self { - Self {} - } -} - -#[cfg(feature = "constant-optimization")] -impl crate::fold::Fold for ConstantOptimizer { - type TargetU = U; - type Error = std::convert::Infallible; - type UserContext = (); - - #[inline(always)] - fn will_map_user(&mut self, _user: &U) -> Self::UserContext {} - #[inline] - fn map_user(&mut self, user: U, _context: ()) -> Result { - Ok(user) - } - fn fold_expr(&mut self, node: crate::Expr) -> Result, Self::Error> { - match node { - crate::Expr::Tuple(crate::ExprTuple { elts, ctx, range }) => { - let elts = elts - .into_iter() - .map(|x| self.fold_expr(x)) - .collect::, _>>()?; - let expr = if elts.iter().all(|e| e.is_constant_expr()) { - let tuple = elts - .into_iter() - .map(|e| match e { - crate::Expr::Constant(crate::ExprConstant { value, .. }) => value, - _ => unreachable!(), - }) - .collect(); - crate::Expr::Constant(crate::ExprConstant { - value: Constant::Tuple(tuple), - kind: None, - range, - }) - } else { - crate::Expr::Tuple(crate::ExprTuple { elts, ctx, range }) - }; - Ok(expr) - } - _ => crate::fold::fold_expr(self, node), - } - } -} - -#[cfg(test)] -mod tests { - use crate::bigint::BigInt; - use rustpython_parser_core::text_size::TextRange; - - #[cfg(feature = "constant-optimization")] - #[test] - fn test_constant_opt() { - use crate::{fold::Fold, *}; - - let range = TextRange::default(); - let ast = ExprTuple { - ctx: ExprContext::Load, - elts: vec![ - ExprConstant { - value: BigInt::from(1).into(), - kind: None, - range, - } - .into(), - ExprConstant { - value: BigInt::from(2).into(), - kind: None, - range, - } - .into(), - ExprTuple { - ctx: ExprContext::Load, - elts: vec![ - ExprConstant { - value: BigInt::from(3).into(), - kind: None, - range, - } - .into(), - ExprConstant { - value: BigInt::from(4).into(), - kind: None, - range, - } - .into(), - ExprConstant { - value: BigInt::from(5).into(), - kind: None, - range, - } - .into(), - ], - range, - } - .into(), - ], - range, - }; - let new_ast = ConstantOptimizer::new() - .fold_expr(ast.into()) - .unwrap_or_else(|e| match e {}); - assert_eq!( - new_ast, - ExprConstant { - value: Constant::Tuple(vec![ - BigInt::from(1).into(), - BigInt::from(2).into(), - Constant::Tuple(vec![ - BigInt::from(3).into(), - BigInt::from(4).into(), - BigInt::from(5).into(), - ]) - ]), - kind: None, - range, - } - .into(), - ); - } -} diff --git a/ast/src/ranged.rs b/ast/src/ranged.rs deleted file mode 100644 index b50e22af..00000000 --- a/ast/src/ranged.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::text_size::{TextRange, TextSize}; - -pub trait Ranged { - fn range(&self) -> TextRange; - - fn start(&self) -> TextSize { - self.range().start() - } - - fn end(&self) -> TextSize { - self.range().end() - } -} - -include!("gen/ranged.rs"); diff --git a/ast/src/source_locator.rs b/ast/src/source_locator.rs deleted file mode 100644 index a72311d0..00000000 --- a/ast/src/source_locator.rs +++ /dev/null @@ -1,326 +0,0 @@ -use crate::Fold; -use rustpython_parser_core::{ - source_code::{LinearLocator, RandomLocator, SourceLocation, SourceRange}, - text_size::TextRange, -}; -use std::{convert::Infallible, unreachable}; - -impl crate::fold::Fold for RandomLocator<'_> { - type TargetU = SourceRange; - type Error = std::convert::Infallible; - type UserContext = SourceLocation; - - fn will_map_user(&mut self, user: &TextRange) -> Self::UserContext { - self.locate(user.start()) - } - - fn map_user( - &mut self, - user: TextRange, - start: Self::UserContext, - ) -> Result { - let end = self.locate(user.end()); - Ok((start..end).into()) - } -} - -fn linear_locate_expr_joined_str( - locator: &mut LinearLocator<'_>, - node: crate::ExprJoinedStr, - location: SourceRange, -) -> Result, Infallible> { - let crate::ExprJoinedStr { range: _, values } = node; - - let mut located_values = Vec::with_capacity(values.len()); - for value in values.into_iter() { - let located = match value { - crate::Expr::Constant(constant) => { - let node = crate::ExprConstant { - range: location, - value: constant.value, - kind: constant.kind, - }; - crate::Expr::Constant(node) - } - crate::Expr::FormattedValue(formatted) => { - let node = crate::ExprFormattedValue { - range: location, - value: locator.fold(formatted.value)?, - conversion: formatted.conversion, - format_spec: formatted - .format_spec - .map(|spec| match *spec { - crate::Expr::JoinedStr(joined_str) => { - let node = - linear_locate_expr_joined_str(locator, joined_str, location)?; - Ok(crate::Expr::JoinedStr(node)) - } - expr => locator.fold(expr), - }) - .transpose()? - .map(Box::new), - }; - crate::Expr::FormattedValue(node) - } - _ => unreachable!("missing expr type for joined_str?"), - }; - located_values.push(located); - } - - Ok(crate::ExprJoinedStr { - range: location, - values: located_values, - }) -} - -impl crate::fold::Fold for LinearLocator<'_> { - type TargetU = SourceRange; - type Error = std::convert::Infallible; - type UserContext = SourceLocation; - - fn will_map_user(&mut self, user: &TextRange) -> Self::UserContext { - self.locate(user.start()) - } - - fn map_user( - &mut self, - user: TextRange, - start: Self::UserContext, - ) -> Result { - let end = self.locate(user.end()); - Ok((start..end).into()) - } - - fn fold_expr_dict( - &mut self, - node: crate::ExprDict, - ) -> Result, Self::Error> { - let crate::ExprDict { - range, - keys, - values, - } = node; - let context = self.will_map_user(&range); - assert_eq!(keys.len(), values.len()); - let mut located_keys = Vec::with_capacity(keys.len()); - let mut located_values = Vec::with_capacity(values.len()); - for (key, value) in keys.into_iter().zip(values.into_iter()) { - located_keys.push(self.fold(key)?); - located_values.push(self.fold(value)?); - } - let range = self.map_user(range, context)?; - Ok(crate::ExprDict { - range, - keys: located_keys, - values: located_values, - }) - } - - fn fold_expr_if_exp( - &mut self, - node: crate::ExprIfExp, - ) -> Result, Self::Error> { - let crate::ExprIfExp { - range, - test, - body, - orelse, - } = node; - let context = self.will_map_user(&range); - let body = self.fold(body)?; - let test = self.fold(test)?; - let orelse = self.fold(orelse)?; - let range = self.map_user(range, context)?; - Ok(crate::ExprIfExp { - range, - test, - body, - orelse, - }) - } - - fn fold_stmt_class_def( - &mut self, - node: crate::StmtClassDef, - ) -> Result, Self::Error> { - let crate::StmtClassDef { - name, - bases, - keywords, - body, - decorator_list, - type_params, - range, - } = node; - let decorator_list = self.fold(decorator_list)?; - let context = self.will_map_user(&range); - - let name = self.fold(name)?; - let type_params = self.fold(type_params)?; - let bases = self.fold(bases)?; - let keywords = self.fold(keywords)?; - let body = self.fold(body)?; - let range = self.map_user(range, context)?; - - Ok(crate::StmtClassDef { - name, - bases, - keywords, - body, - decorator_list, - type_params, - range, - }) - } - fn fold_stmt_function_def( - &mut self, - node: crate::StmtFunctionDef, - ) -> Result, Self::Error> { - let crate::StmtFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_comment, - range, - type_params, - } = node; - let decorator_list = self.fold(decorator_list)?; - let context = self.will_map_user(&range); - - let name = self.fold(name)?; - let type_params = self.fold(type_params)?; - let args: Box> = self.fold(args)?; - let returns = self.fold(returns)?; - let body = self.fold(body)?; - let type_comment = self.fold(type_comment)?; - let range = self.map_user(range, context)?; - Ok(crate::StmtFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_params, - type_comment, - range, - }) - } - fn fold_stmt_async_function_def( - &mut self, - node: crate::StmtAsyncFunctionDef, - ) -> Result, Self::Error> { - let crate::StmtAsyncFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_comment, - type_params, - range, - } = node; - let decorator_list = self.fold(decorator_list)?; - let context = self.will_map_user(&range); - - let name = self.fold(name)?; - let type_params = self.fold(type_params)?; - let args: Box> = self.fold(args)?; - let returns = self.fold(returns)?; - let body = self.fold(body)?; - let type_comment = self.fold(type_comment)?; - let range = self.map_user(range, context)?; - Ok(crate::StmtAsyncFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_comment, - type_params, - range, - }) - } - fn fold_expr_joined_str( - &mut self, - node: crate::ExprJoinedStr, - ) -> Result, Self::Error> { - let start = self.locate(node.range.start()); - let end = self.locate_only(node.range.end()); - let location = SourceRange::new(start, end); - linear_locate_expr_joined_str(self, node, location) - } - - fn fold_expr_call( - &mut self, - node: crate::ExprCall, - ) -> Result, Self::Error> { - let crate::ExprCall { - range, - func, - args, - keywords, - } = node; - let context = self.will_map_user(&range); - let func = self.fold(func)?; - let keywords = LinearLookaheadLocator(self).fold(keywords)?; - let args = self.fold(args)?; - let range = self.map_user(range, context)?; - Ok(crate::ExprCall { - range, - func, - args, - keywords, - }) - } - - fn fold_pattern_match_mapping( - &mut self, - node: crate::PatternMatchMapping, - ) -> Result, Self::Error> { - let crate::PatternMatchMapping { - keys, - patterns, - rest, - range, - } = node; - let context = self.will_map_user(&range); - - let mut located_keys = Vec::with_capacity(keys.len()); - let mut located_patterns = Vec::with_capacity(patterns.len()); - for (key, value) in keys.into_iter().zip(patterns.into_iter()) { - located_keys.push(self.fold(key)?); - located_patterns.push(self.fold(value)?); - } - let rest = self.fold(rest)?; - let range = self.map_user(range, context)?; - Ok(crate::PatternMatchMapping { - keys: located_keys, - patterns: located_patterns, - rest, - range, - }) - } -} - -struct LinearLookaheadLocator<'a, 'b>(&'b mut LinearLocator<'a>); - -impl crate::fold::Fold for LinearLookaheadLocator<'_, '_> { - type TargetU = SourceRange; - type Error = std::convert::Infallible; - type UserContext = SourceLocation; - - fn will_map_user(&mut self, user: &TextRange) -> Self::UserContext { - self.0.locate_only(user.start()) - } - - fn map_user( - &mut self, - user: TextRange, - start: Self::UserContext, - ) -> Result { - let end = self.0.locate_only(user.end()); - Ok((start..end).into()) - } -} diff --git a/ast/src/unparse.rs b/ast/src/unparse.rs index ae4a2d16..40570fc7 100644 --- a/ast/src/unparse.rs +++ b/ast/src/unparse.rs @@ -1,6 +1,6 @@ use crate::{ - Arg, ArgWithDefault, Arguments, BoolOp, Comprehension, Constant, ConversionFlag, Expr, - Identifier, Operator, PythonArguments, + Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, ConversionFlag, Expr, ExprKind, + Operator, }; use std::fmt; @@ -34,9 +34,6 @@ impl<'a> Unparser<'a> { fn p(&mut self, s: &str) -> fmt::Result { self.f.write_str(s) } - fn p_id(&mut self, s: &Identifier) -> fmt::Result { - self.f.write_str(s.as_str()) - } fn p_if(&mut self, cond: bool, s: &str) -> fmt::Result { if cond { self.f.write_str(s)?; @@ -73,13 +70,9 @@ impl<'a> Unparser<'a> { ret }}; } - match &ast { - Expr::BoolOp(crate::ExprBoolOp { - op, - values, - range: _range, - }) => { - let (op, prec) = op_prec!(bin, op, BoolOp, And("and", AND), Or("or", OR)); + match &ast.node { + ExprKind::BoolOp { op, values } => { + let (op, prec) = op_prec!(bin, op, Boolop, And("and", AND), Or("or", OR)); group_if!(prec, { let mut first = true; for val in values { @@ -88,23 +81,14 @@ impl<'a> Unparser<'a> { } }) } - Expr::NamedExpr(crate::ExprNamedExpr { - target, - value, - range: _range, - }) => { + ExprKind::NamedExpr { target, value } => { group_if!(precedence::TUPLE, { self.unparse_expr(target, precedence::ATOM)?; self.p(" := ")?; self.unparse_expr(value, precedence::ATOM)?; }) } - Expr::BinOp(crate::ExprBinOp { - left, - op, - right, - range: _range, - }) => { + ExprKind::BinOp { left, op, right } => { let right_associative = matches!(op, Operator::Pow); let (op, prec) = op_prec!( bin, @@ -130,15 +114,11 @@ impl<'a> Unparser<'a> { self.unparse_expr(right, prec + !right_associative as u8)?; }) } - Expr::UnaryOp(crate::ExprUnaryOp { - op, - operand, - range: _range, - }) => { + ExprKind::UnaryOp { op, operand } => { let (op, prec) = op_prec!( un, op, - crate::UnaryOp, + crate::Unaryop, Invert("~", FACTOR), Not("not ", NOT), UAdd("+", FACTOR), @@ -149,24 +129,15 @@ impl<'a> Unparser<'a> { self.unparse_expr(operand, prec)?; }) } - Expr::Lambda(crate::ExprLambda { - args, - body, - range: _range, - }) => { + ExprKind::Lambda { args, body } => { group_if!(precedence::TEST, { let pos = args.args.len() + args.posonlyargs.len(); self.p(if pos > 0 { "lambda " } else { "lambda" })?; - self.unparse_arguments(args)?; + self.unparse_args(args)?; write!(self, ": {}", **body)?; }) } - Expr::IfExp(crate::ExprIfExp { - test, - body, - orelse, - range: _range, - }) => { + ExprKind::IfExp { test, body, orelse } => { group_if!(precedence::TEST, { self.unparse_expr(body, precedence::TEST + 1)?; self.p(" if ")?; @@ -175,11 +146,7 @@ impl<'a> Unparser<'a> { self.unparse_expr(orelse, precedence::TEST)?; }) } - Expr::Dict(crate::ExprDict { - keys, - values, - range: _range, - }) => { + ExprKind::Dict { keys, values } => { self.p("{")?; let mut first = true; let (packed, unpacked) = values.split_at(keys.len()); @@ -197,10 +164,7 @@ impl<'a> Unparser<'a> { } self.p("}")?; } - Expr::Set(crate::ExprSet { - elts, - range: _range, - }) => { + ExprKind::Set { elts } => { self.p("{")?; let mut first = true; for v in elts { @@ -209,32 +173,23 @@ impl<'a> Unparser<'a> { } self.p("}")?; } - Expr::ListComp(crate::ExprListComp { - elt, - generators, - range: _range, - }) => { + ExprKind::ListComp { elt, generators } => { self.p("[")?; self.unparse_expr(elt, precedence::TEST)?; self.unparse_comp(generators)?; self.p("]")?; } - Expr::SetComp(crate::ExprSetComp { - elt, - generators, - range: _range, - }) => { + ExprKind::SetComp { elt, generators } => { self.p("{")?; self.unparse_expr(elt, precedence::TEST)?; self.unparse_comp(generators)?; self.p("}")?; } - Expr::DictComp(crate::ExprDictComp { + ExprKind::DictComp { key, value, generators, - range: _range, - }) => { + } => { self.p("{")?; self.unparse_expr(key, precedence::TEST)?; self.p(": ")?; @@ -242,72 +197,66 @@ impl<'a> Unparser<'a> { self.unparse_comp(generators)?; self.p("}")?; } - Expr::GeneratorExp(crate::ExprGeneratorExp { - elt, - generators, - range: _range, - }) => { + ExprKind::GeneratorExp { elt, generators } => { self.p("(")?; self.unparse_expr(elt, precedence::TEST)?; self.unparse_comp(generators)?; self.p(")")?; } - Expr::Await(crate::ExprAwait { - value, - range: _range, - }) => { + ExprKind::Await { value } => { group_if!(precedence::AWAIT, { self.p("await ")?; self.unparse_expr(value, precedence::ATOM)?; }) } - Expr::Yield(crate::ExprYield { - value, - range: _range, - }) => { + ExprKind::Yield { value } => { if let Some(value) = value { write!(self, "(yield {})", **value)?; } else { self.p("(yield)")?; } } - Expr::YieldFrom(crate::ExprYieldFrom { - value, - range: _range, - }) => { + ExprKind::YieldFrom { value } => { write!(self, "(yield from {})", **value)?; } - Expr::Compare(crate::ExprCompare { + ExprKind::Compare { left, ops, comparators, - range: _range, - }) => { + } => { group_if!(precedence::CMP, { let new_lvl = precedence::CMP + 1; self.unparse_expr(left, new_lvl)?; for (op, cmp) in ops.iter().zip(comparators) { - self.p(" ")?; - self.p(op.as_str())?; - self.p(" ")?; + let op = match op { + Cmpop::Eq => " == ", + Cmpop::NotEq => " != ", + Cmpop::Lt => " < ", + Cmpop::LtE => " <= ", + Cmpop::Gt => " > ", + Cmpop::GtE => " >= ", + Cmpop::Is => " is ", + Cmpop::IsNot => " is not ", + Cmpop::In => " in ", + Cmpop::NotIn => " not in ", + }; + self.p(op)?; self.unparse_expr(cmp, new_lvl)?; } }) } - Expr::Call(crate::ExprCall { + ExprKind::Call { func, args, keywords, - range: _range, - }) => { + } => { self.unparse_expr(func, precedence::ATOM)?; self.p("(")?; if let ( - [Expr::GeneratorExp(crate::ExprGeneratorExp { - elt, - generators, - range: _range, - })], + [Expr { + node: ExprKind::GeneratorExp { elt, generators }, + .. + }], [], ) = (&**args, &**keywords) { @@ -322,32 +271,24 @@ impl<'a> Unparser<'a> { } for kw in keywords { self.p_delim(&mut first, ", ")?; - if let Some(arg) = &kw.arg { - self.p_id(arg)?; + if let Some(arg) = &kw.node.arg { + self.p(arg)?; self.p("=")?; } else { self.p("**")?; } - self.unparse_expr(&kw.value, precedence::TEST)?; + self.unparse_expr(&kw.node.value, precedence::TEST)?; } } self.p(")")?; } - Expr::FormattedValue(crate::ExprFormattedValue { + ExprKind::FormattedValue { value, conversion, format_spec, - range: _range, - }) => self.unparse_formatted(value, *conversion, format_spec.as_deref())?, - Expr::JoinedStr(crate::ExprJoinedStr { - values, - range: _range, - }) => self.unparse_joined_str(values, false)?, - Expr::Constant(crate::ExprConstant { - value, - kind, - range: _range, - }) => { + } => self.unparse_formatted(value, *conversion, format_spec.as_deref())?, + ExprKind::JoinedStr { values } => self.unparse_joined_str(values, false)?, + ExprKind::Constant { value, kind } => { if let Some(kind) = kind { self.p(kind)?; } @@ -363,33 +304,41 @@ impl<'a> Unparser<'a> { _ => fmt::Display::fmt(value, &mut self.f)?, } } - Expr::Attribute(crate::ExprAttribute { value, attr, .. }) => { + ExprKind::Attribute { value, attr, .. } => { self.unparse_expr(value, precedence::ATOM)?; - let period = if let Expr::Constant(crate::ExprConstant { + let period = if let ExprKind::Constant { value: Constant::Int(_), .. - }) = value.as_ref() + } = &value.node { " ." } else { "." }; self.p(period)?; - self.p_id(attr)?; + self.p(attr)?; } - Expr::Subscript(crate::ExprSubscript { value, slice, .. }) => { + ExprKind::Subscript { value, slice, .. } => { self.unparse_expr(value, precedence::ATOM)?; - let lvl = precedence::TUPLE; + let mut lvl = precedence::TUPLE; + if let ExprKind::Tuple { elts, .. } = &slice.node { + if elts + .iter() + .any(|expr| matches!(expr.node, ExprKind::Starred { .. })) + { + lvl += 1 + } + } self.p("[")?; self.unparse_expr(slice, lvl)?; self.p("]")?; } - Expr::Starred(crate::ExprStarred { value, .. }) => { + ExprKind::Starred { value, .. } => { self.p("*")?; self.unparse_expr(value, precedence::EXPR)?; } - Expr::Name(crate::ExprName { id, .. }) => self.p_id(id)?, - Expr::List(crate::ExprList { elts, .. }) => { + ExprKind::Name { id, .. } => self.p(id)?, + ExprKind::List { elts, .. } => { self.p("[")?; let mut first = true; for elt in elts { @@ -398,7 +347,7 @@ impl<'a> Unparser<'a> { } self.p("]")?; } - Expr::Tuple(crate::ExprTuple { elts, .. }) => { + ExprKind::Tuple { elts, .. } => { if elts.is_empty() { self.p("()")?; } else { @@ -412,12 +361,7 @@ impl<'a> Unparser<'a> { }) } } - Expr::Slice(crate::ExprSlice { - lower, - upper, - step, - range: _range, - }) => { + ExprKind::Slice { lower, upper, step } => { if let Some(lower) = lower { self.unparse_expr(lower, precedence::TEST)?; } @@ -434,44 +378,7 @@ impl<'a> Unparser<'a> { Ok(()) } - fn unparse_arguments(&mut self, args: &Arguments) -> fmt::Result { - let mut first = true; - for (i, arg) in args.posonlyargs.iter().chain(&args.args).enumerate() { - self.p_delim(&mut first, ", ")?; - self.unparse_function_arg(arg)?; - self.p_if(i + 1 == args.posonlyargs.len(), ", /")?; - } - if args.vararg.is_some() || !args.kwonlyargs.is_empty() { - self.p_delim(&mut first, ", ")?; - self.p("*")?; - } - if let Some(vararg) = &args.vararg { - self.unparse_arg(vararg)?; - } - for kwarg in args.kwonlyargs.iter() { - self.p_delim(&mut first, ", ")?; - self.unparse_function_arg(kwarg)?; - } - if let Some(kwarg) = &args.kwarg { - self.p_delim(&mut first, ", ")?; - self.p("**")?; - self.unparse_arg(kwarg)?; - } - Ok(()) - } - fn unparse_function_arg(&mut self, arg: &ArgWithDefault) -> fmt::Result { - self.p_id(&arg.def.arg)?; - if let Some(ann) = &arg.def.annotation { - write!(self, ": {}", **ann)?; - } - if let Some(default) = &arg.default { - write!(self, "={}", default)?; - } - Ok(()) - } - - #[allow(dead_code)] - fn unparse_python_arguments(&mut self, args: &PythonArguments) -> fmt::Result { + fn unparse_args(&mut self, args: &Arguments) -> fmt::Result { let mut first = true; let defaults_start = args.posonlyargs.len() + args.args.len() - args.defaults.len(); for (i, arg) in args.posonlyargs.iter().chain(&args.args).enumerate() { @@ -508,8 +415,8 @@ impl<'a> Unparser<'a> { Ok(()) } fn unparse_arg(&mut self, arg: &Arg) -> fmt::Result { - self.p_id(&arg.arg)?; - if let Some(ann) = &arg.annotation { + self.p(&arg.node.arg)?; + if let Some(ann) = &arg.node.annotation { write!(self, ": {}", **ann)?; } Ok(()) @@ -517,7 +424,7 @@ impl<'a> Unparser<'a> { fn unparse_comp(&mut self, generators: &[Comprehension]) -> fmt::Result { for comp in generators { - self.p(if comp.is_async { + self.p(if comp.is_async > 0 { " async for " } else { " for " @@ -543,7 +450,7 @@ impl<'a> Unparser<'a> { fn unparse_formatted( &mut self, val: &Expr, - conversion: ConversionFlag, + conversion: usize, spec: Option<&Expr>, ) -> fmt::Result { let buffered = to_string_fmt(|f| Unparser::new(f).unparse_expr(val, precedence::TEST + 1)); @@ -557,7 +464,7 @@ impl<'a> Unparser<'a> { self.p(&buffered)?; drop(buffered); - if conversion != ConversionFlag::None { + if conversion != ConversionFlag::None as usize { self.p("!")?; let buf = &[conversion as u8]; let c = std::str::from_utf8(buf).unwrap(); @@ -575,24 +482,20 @@ impl<'a> Unparser<'a> { } fn unparse_fstring_elem(&mut self, expr: &Expr, is_spec: bool) -> fmt::Result { - match &expr { - Expr::Constant(crate::ExprConstant { value, .. }) => { + match &expr.node { + ExprKind::Constant { value, .. } => { if let Constant::Str(s) = value { self.unparse_fstring_str(s) } else { unreachable!() } } - Expr::JoinedStr(crate::ExprJoinedStr { - values, - range: _range, - }) => self.unparse_joined_str(values, is_spec), - Expr::FormattedValue(crate::ExprFormattedValue { + ExprKind::JoinedStr { values } => self.unparse_joined_str(values, is_spec), + ExprKind::FormattedValue { value, conversion, format_spec, - range: _range, - }) => self.unparse_formatted(value, *conversion, format_spec.as_deref()), + } => self.unparse_formatted(value, *conversion, format_spec.as_deref()), _ => unreachable!(), } } diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml new file mode 100644 index 00000000..3bb891ed --- /dev/null +++ b/codegen/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "rustpython-codegen" +version = "0.2.0" +description = "Compiler for python code into bytecode for the rustpython VM." +authors = ["RustPython Team"] +repository = "https://github.com/RustPython/RustPython" +license = "MIT" +edition = "2021" + +[dependencies] +rustpython-ast = { path = "../ast", features = ["unparse"] } +rustpython-compiler-core = { path = "../core", version = "0.2.0" } + +ahash = { workspace = true } +bitflags = { workspace = true } +indexmap = { workspace = true } +itertools = { workspace = true } +log = { workspace = true } +num-complex = { workspace = true } +num-traits = { workspace = true } + +[dev-dependencies] +rustpython-parser = { path = "../parser" } + +insta = { workspace = true } diff --git a/codegen/src/compile.rs b/codegen/src/compile.rs new file mode 100644 index 00000000..4d940961 --- /dev/null +++ b/codegen/src/compile.rs @@ -0,0 +1,2937 @@ +//! +//! Take an AST and transform it into bytecode +//! +//! Inspirational code: +//! +//! + +#![deny(clippy::cast_possible_truncation)] + +use crate::{ + error::{CodegenError, CodegenErrorType}, + ir, + symboltable::{self, SymbolFlags, SymbolScope, SymbolTable}, + IndexSet, +}; +use itertools::Itertools; +use num_complex::Complex64; +use num_traits::ToPrimitive; +use rustpython_ast as ast; +use rustpython_compiler_core::{ + self as bytecode, Arg as OpArgMarker, CodeObject, ConstantData, Instruction, Location, OpArg, + OpArgType, +}; +use std::borrow::Cow; + +pub use rustpython_compiler_core::Mode; + +type CompileResult = Result; + +#[derive(PartialEq, Eq, Clone, Copy)] +enum NameUsage { + Load, + Store, + Delete, +} + +enum CallType { + Positional { nargs: u32 }, + Keyword { nargs: u32 }, + Ex { has_kwargs: bool }, +} + +fn is_forbidden_name(name: &str) -> bool { + // See https://docs.python.org/3/library/constants.html#built-in-constants + const BUILTIN_CONSTANTS: &[&str] = &["__debug__"]; + + BUILTIN_CONSTANTS.contains(&name) +} + +/// Main structure holding the state of compilation. +struct Compiler { + code_stack: Vec, + symbol_table_stack: Vec, + source_path: String, + current_source_location: Location, + qualified_path: Vec, + done_with_future_stmts: bool, + future_annotations: bool, + ctx: CompileContext, + class_name: Option, + opts: CompileOpts, +} + +#[derive(Debug, Clone, Default)] +pub struct CompileOpts { + /// How optimized the bytecode output should be; any optimize > 0 does + /// not emit assert statements + pub optimize: u8, +} + +#[derive(Debug, Clone, Copy)] +struct CompileContext { + loop_data: Option<(ir::BlockIdx, ir::BlockIdx)>, + in_class: bool, + func: FunctionContext, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +enum FunctionContext { + NoFunction, + Function, + AsyncFunction, +} + +impl CompileContext { + fn in_func(self) -> bool { + self.func != FunctionContext::NoFunction + } +} + +/// Compile an ast::Mod produced from rustpython_parser::parse() +pub fn compile_top( + ast: &ast::Mod, + source_path: String, + mode: Mode, + opts: CompileOpts, +) -> CompileResult { + match ast { + ast::Mod::Module { body, .. } => compile_program(body, source_path, opts), + ast::Mod::Interactive { body } => match mode { + Mode::Single => compile_program_single(body, source_path, opts), + Mode::BlockExpr => compile_block_expression(body, source_path, opts), + _ => unreachable!("only Single and BlockExpr parsed to Interactive"), + }, + ast::Mod::Expression { body } => compile_expression(body, source_path, opts), + ast::Mod::FunctionType { .. } => panic!("can't compile a FunctionType"), + } +} + +/// A helper function for the shared code of the different compile functions +fn compile_impl( + ast: &Ast, + source_path: String, + opts: CompileOpts, + make_symbol_table: impl FnOnce(&Ast) -> Result, + compile: impl FnOnce(&mut Compiler, &Ast, SymbolTable) -> CompileResult<()>, +) -> CompileResult { + let symbol_table = match make_symbol_table(ast) { + Ok(x) => x, + Err(e) => return Err(e.into_codegen_error(source_path)), + }; + + let mut compiler = Compiler::new(opts, source_path, "".to_owned()); + compile(&mut compiler, ast, symbol_table)?; + let code = compiler.pop_code_object(); + trace!("Compilation completed: {:?}", code); + Ok(code) +} + +/// Compile a standard Python program to bytecode +pub fn compile_program( + ast: &[ast::Stmt], + source_path: String, + opts: CompileOpts, +) -> CompileResult { + compile_impl( + ast, + source_path, + opts, + SymbolTable::scan_program, + Compiler::compile_program, + ) +} + +/// Compile a Python program to bytecode for the context of a REPL +pub fn compile_program_single( + ast: &[ast::Stmt], + source_path: String, + opts: CompileOpts, +) -> CompileResult { + compile_impl( + ast, + source_path, + opts, + SymbolTable::scan_program, + Compiler::compile_program_single, + ) +} + +pub fn compile_block_expression( + ast: &[ast::Stmt], + source_path: String, + opts: CompileOpts, +) -> CompileResult { + compile_impl( + ast, + source_path, + opts, + SymbolTable::scan_program, + Compiler::compile_block_expr, + ) +} + +pub fn compile_expression( + ast: &ast::Expr, + source_path: String, + opts: CompileOpts, +) -> CompileResult { + compile_impl( + ast, + source_path, + opts, + SymbolTable::scan_expr, + Compiler::compile_eval, + ) +} + +macro_rules! emit { + ($c:expr, Instruction::$op:ident { $arg:ident$(,)? }$(,)?) => { + $c.emit_arg($arg, |x| Instruction::$op { $arg: x }) + }; + ($c:expr, Instruction::$op:ident { $arg:ident : $arg_val:expr $(,)? }$(,)?) => { + $c.emit_arg($arg_val, |x| Instruction::$op { $arg: x }) + }; + ($c:expr, Instruction::$op:ident( $arg_val:expr $(,)? )$(,)?) => { + $c.emit_arg($arg_val, Instruction::$op) + }; + ($c:expr, Instruction::$op:ident$(,)?) => { + $c.emit_no_arg(Instruction::$op) + }; +} + +impl Compiler { + fn new(opts: CompileOpts, source_path: String, code_name: String) -> Self { + let module_code = ir::CodeInfo { + flags: bytecode::CodeFlags::NEW_LOCALS, + posonlyarg_count: 0, + arg_count: 0, + kwonlyarg_count: 0, + source_path: source_path.clone(), + first_line_number: 0, + obj_name: code_name, + + blocks: vec![ir::Block::default()], + current_block: ir::BlockIdx(0), + constants: IndexSet::default(), + name_cache: IndexSet::default(), + varname_cache: IndexSet::default(), + cellvar_cache: IndexSet::default(), + freevar_cache: IndexSet::default(), + }; + Compiler { + code_stack: vec![module_code], + symbol_table_stack: Vec::new(), + source_path, + current_source_location: Location::default(), + qualified_path: Vec::new(), + done_with_future_stmts: false, + future_annotations: false, + ctx: CompileContext { + loop_data: None, + in_class: false, + func: FunctionContext::NoFunction, + }, + class_name: None, + opts, + } + } + + fn error(&self, error: CodegenErrorType) -> CodegenError { + self.error_loc(error, self.current_source_location) + } + fn error_loc(&self, error: CodegenErrorType, location: Location) -> CodegenError { + CodegenError { + error, + location, + source_path: self.source_path.clone(), + } + } + + fn push_output( + &mut self, + flags: bytecode::CodeFlags, + posonlyarg_count: u32, + arg_count: u32, + kwonlyarg_count: u32, + obj_name: String, + ) { + let source_path = self.source_path.clone(); + let first_line_number = self.get_source_line_number(); + + let table = self + .symbol_table_stack + .last_mut() + .unwrap() + .sub_tables + .remove(0); + + let cellvar_cache = table + .symbols + .iter() + .filter(|(_, s)| s.scope == SymbolScope::Cell) + .map(|(var, _)| var.clone()) + .collect(); + let freevar_cache = table + .symbols + .iter() + .filter(|(_, s)| { + s.scope == SymbolScope::Free || s.flags.contains(SymbolFlags::FREE_CLASS) + }) + .map(|(var, _)| var.clone()) + .collect(); + + self.symbol_table_stack.push(table); + + let info = ir::CodeInfo { + flags, + posonlyarg_count, + arg_count, + kwonlyarg_count, + source_path, + first_line_number, + obj_name, + + blocks: vec![ir::Block::default()], + current_block: ir::BlockIdx(0), + constants: IndexSet::default(), + name_cache: IndexSet::default(), + varname_cache: IndexSet::default(), + cellvar_cache, + freevar_cache, + }; + self.code_stack.push(info); + } + + fn pop_code_object(&mut self) -> CodeObject { + let table = self.symbol_table_stack.pop().unwrap(); + assert!(table.sub_tables.is_empty()); + self.code_stack + .pop() + .unwrap() + .finalize_code(self.opts.optimize) + } + + // could take impl Into>, but everything is borrowed from ast structs; we never + // actually have a `String` to pass + fn name(&mut self, name: &str) -> bytecode::NameIdx { + self._name_inner(name, |i| &mut i.name_cache) + } + fn varname(&mut self, name: &str) -> CompileResult { + if Compiler::is_forbidden_arg_name(name) { + return Err(self.error(CodegenErrorType::SyntaxError(format!( + "cannot assign to {name}", + )))); + } + Ok(self._name_inner(name, |i| &mut i.varname_cache)) + } + fn _name_inner( + &mut self, + name: &str, + cache: impl FnOnce(&mut ir::CodeInfo) -> &mut IndexSet, + ) -> bytecode::NameIdx { + let name = self.mangle(name); + let cache = cache(self.current_code_info()); + cache + .get_index_of(name.as_ref()) + .unwrap_or_else(|| cache.insert_full(name.into_owned()).0) + .to_u32() + } + + fn compile_program( + &mut self, + body: &[ast::Stmt], + symbol_table: SymbolTable, + ) -> CompileResult<()> { + let size_before = self.code_stack.len(); + self.symbol_table_stack.push(symbol_table); + + let (doc, statements) = split_doc(body); + if let Some(value) = doc { + self.emit_constant(ConstantData::Str { value }); + let doc = self.name("__doc__"); + emit!(self, Instruction::StoreGlobal(doc)) + } + + if Self::find_ann(statements) { + emit!(self, Instruction::SetupAnnotation); + } + + self.compile_statements(statements)?; + + assert_eq!(self.code_stack.len(), size_before); + + // Emit None at end: + self.emit_constant(ConstantData::None); + emit!(self, Instruction::ReturnValue); + Ok(()) + } + + fn compile_program_single( + &mut self, + body: &[ast::Stmt], + symbol_table: SymbolTable, + ) -> CompileResult<()> { + self.symbol_table_stack.push(symbol_table); + + if let Some((last, body)) = body.split_last() { + for statement in body { + if let ast::StmtKind::Expr { value } = &statement.node { + self.compile_expression(value)?; + emit!(self, Instruction::PrintExpr); + } else { + self.compile_statement(statement)?; + } + } + + if let ast::StmtKind::Expr { value } = &last.node { + self.compile_expression(value)?; + emit!(self, Instruction::Duplicate); + emit!(self, Instruction::PrintExpr); + } else { + self.compile_statement(last)?; + self.emit_constant(ConstantData::None); + } + } else { + self.emit_constant(ConstantData::None); + }; + + emit!(self, Instruction::ReturnValue); + Ok(()) + } + + fn compile_block_expr( + &mut self, + body: &[ast::Stmt], + symbol_table: SymbolTable, + ) -> CompileResult<()> { + self.symbol_table_stack.push(symbol_table); + + self.compile_statements(body)?; + + if let Some(last_statement) = body.last() { + match last_statement.node { + ast::StmtKind::Expr { .. } => { + self.current_block().instructions.pop(); // pop Instruction::Pop + } + ast::StmtKind::FunctionDef { .. } + | ast::StmtKind::AsyncFunctionDef { .. } + | ast::StmtKind::ClassDef { .. } => { + let store_inst = self.current_block().instructions.pop().unwrap(); // pop Instruction::Store + emit!(self, Instruction::Duplicate); + self.current_block().instructions.push(store_inst); + } + _ => self.emit_constant(ConstantData::None), + } + } + emit!(self, Instruction::ReturnValue); + + Ok(()) + } + + // Compile statement in eval mode: + fn compile_eval( + &mut self, + expression: &ast::Expr, + symbol_table: SymbolTable, + ) -> CompileResult<()> { + self.symbol_table_stack.push(symbol_table); + self.compile_expression(expression)?; + emit!(self, Instruction::ReturnValue); + Ok(()) + } + + fn compile_statements(&mut self, statements: &[ast::Stmt]) -> CompileResult<()> { + for statement in statements { + self.compile_statement(statement)? + } + Ok(()) + } + + fn load_name(&mut self, name: &str) -> CompileResult<()> { + self.compile_name(name, NameUsage::Load) + } + + fn store_name(&mut self, name: &str) -> CompileResult<()> { + self.compile_name(name, NameUsage::Store) + } + + fn mangle<'a>(&self, name: &'a str) -> Cow<'a, str> { + symboltable::mangle_name(self.class_name.as_deref(), name) + } + + fn check_forbidden_name(&self, name: &str, usage: NameUsage) -> CompileResult<()> { + let msg = match usage { + NameUsage::Store if is_forbidden_name(name) => "cannot assign to", + NameUsage::Delete if is_forbidden_name(name) => "cannot delete", + _ => return Ok(()), + }; + Err(self.error(CodegenErrorType::SyntaxError(format!("{msg} {name}")))) + } + + fn compile_name(&mut self, name: &str, usage: NameUsage) -> CompileResult<()> { + let name = self.mangle(name); + + self.check_forbidden_name(&name, usage)?; + + let symbol_table = self.symbol_table_stack.last().unwrap(); + let symbol = symbol_table.lookup(name.as_ref()).expect( + "The symbol must be present in the symbol table, even when it is undefined in python.", + ); + let info = self.code_stack.last_mut().unwrap(); + let mut cache = &mut info.name_cache; + enum NameOpType { + Fast, + Global, + Deref, + Local, + } + let op_typ = match symbol.scope { + SymbolScope::Local if self.ctx.in_func() => { + cache = &mut info.varname_cache; + NameOpType::Fast + } + SymbolScope::GlobalExplicit => NameOpType::Global, + SymbolScope::GlobalImplicit | SymbolScope::Unknown if self.ctx.in_func() => { + NameOpType::Global + } + SymbolScope::GlobalImplicit | SymbolScope::Unknown => NameOpType::Local, + SymbolScope::Local => NameOpType::Local, + SymbolScope::Free => { + cache = &mut info.freevar_cache; + NameOpType::Deref + } + SymbolScope::Cell => { + cache = &mut info.cellvar_cache; + NameOpType::Deref + } + // // TODO: is this right? + // SymbolScope::Unknown => NameOpType::Global, + }; + + if NameUsage::Load == usage && name == "__debug__" { + self.emit_constant(ConstantData::Boolean { + value: self.opts.optimize == 0, + }); + return Ok(()); + } + + let mut idx = cache + .get_index_of(name.as_ref()) + .unwrap_or_else(|| cache.insert_full(name.into_owned()).0); + if let SymbolScope::Free = symbol.scope { + idx += info.cellvar_cache.len(); + } + let op = match op_typ { + NameOpType::Fast => match usage { + NameUsage::Load => Instruction::LoadFast, + NameUsage::Store => Instruction::StoreFast, + NameUsage::Delete => Instruction::DeleteFast, + }, + NameOpType::Global => match usage { + NameUsage::Load => Instruction::LoadGlobal, + NameUsage::Store => Instruction::StoreGlobal, + NameUsage::Delete => Instruction::DeleteGlobal, + }, + NameOpType::Deref => match usage { + NameUsage::Load if !self.ctx.in_func() && self.ctx.in_class => { + Instruction::LoadClassDeref + } + NameUsage::Load => Instruction::LoadDeref, + NameUsage::Store => Instruction::StoreDeref, + NameUsage::Delete => Instruction::DeleteDeref, + }, + NameOpType::Local => match usage { + NameUsage::Load => Instruction::LoadNameAny, + NameUsage::Store => Instruction::StoreLocal, + NameUsage::Delete => Instruction::DeleteLocal, + }, + }; + self.emit_arg(idx.to_u32(), op); + + Ok(()) + } + + fn compile_statement(&mut self, statement: &ast::Stmt) -> CompileResult<()> { + trace!("Compiling {:?}", statement); + self.set_source_location(statement.location); + use ast::StmtKind::*; + + match &statement.node { + // we do this here because `from __future__` still executes that `from` statement at runtime, + // we still need to compile the ImportFrom down below + ImportFrom { module, names, .. } if module.as_deref() == Some("__future__") => { + self.compile_future_features(names)? + } + // if we find any other statement, stop accepting future statements + _ => self.done_with_future_stmts = true, + } + + match &statement.node { + Import { names } => { + // import a, b, c as d + for name in names { + let name = &name.node; + self.emit_constant(ConstantData::Integer { + value: num_traits::Zero::zero(), + }); + self.emit_constant(ConstantData::None); + let idx = self.name(&name.name); + emit!(self, Instruction::ImportName { idx }); + if let Some(alias) = &name.asname { + for part in name.name.split('.').skip(1) { + let idx = self.name(part); + emit!(self, Instruction::LoadAttr { idx }); + } + self.store_name(alias)? + } else { + self.store_name(name.name.split('.').next().unwrap())? + } + } + } + ImportFrom { + level, + module, + names, + } => { + let import_star = names.iter().any(|n| n.node.name == "*"); + + let from_list = if import_star { + if self.ctx.in_func() { + return Err(self + .error_loc(CodegenErrorType::FunctionImportStar, statement.location)); + } + vec![ConstantData::Str { + value: "*".to_owned(), + }] + } else { + names + .iter() + .map(|n| ConstantData::Str { + value: n.node.name.to_owned(), + }) + .collect() + }; + + let module_idx = module.as_ref().map(|s| self.name(s)); + + // from .... import (*fromlist) + self.emit_constant(ConstantData::Integer { + value: (*level).unwrap_or(0).into(), + }); + self.emit_constant(ConstantData::Tuple { + elements: from_list, + }); + if let Some(idx) = module_idx { + emit!(self, Instruction::ImportName { idx }); + } else { + emit!(self, Instruction::ImportNameless); + } + + if import_star { + // from .... import * + emit!(self, Instruction::ImportStar); + } else { + // from mod import a, b as c + + for name in names { + let name = &name.node; + let idx = self.name(&name.name); + // import symbol from module: + emit!(self, Instruction::ImportFrom { idx }); + + // Store module under proper name: + if let Some(alias) = &name.asname { + self.store_name(alias)? + } else { + self.store_name(&name.name)? + } + } + + // Pop module from stack: + emit!(self, Instruction::Pop); + } + } + Expr { value } => { + self.compile_expression(value)?; + + // Pop result of stack, since we not use it: + emit!(self, Instruction::Pop); + } + Global { .. } | Nonlocal { .. } => { + // Handled during symbol table construction. + } + If { test, body, orelse } => { + let after_block = self.new_block(); + if orelse.is_empty() { + // Only if: + self.compile_jump_if(test, false, after_block)?; + self.compile_statements(body)?; + } else { + // if - else: + let else_block = self.new_block(); + self.compile_jump_if(test, false, else_block)?; + self.compile_statements(body)?; + emit!( + self, + Instruction::Jump { + target: after_block, + } + ); + + // else: + self.switch_to_block(else_block); + self.compile_statements(orelse)?; + } + self.switch_to_block(after_block); + } + While { test, body, orelse } => self.compile_while(test, body, orelse)?, + With { items, body, .. } => self.compile_with(items, body, false)?, + AsyncWith { items, body, .. } => self.compile_with(items, body, true)?, + For { + target, + iter, + body, + orelse, + .. + } => self.compile_for(target, iter, body, orelse, false)?, + AsyncFor { + target, + iter, + body, + orelse, + .. + } => self.compile_for(target, iter, body, orelse, true)?, + Match { subject, cases } => self.compile_match(subject, cases)?, + Raise { exc, cause } => { + let kind = match exc { + Some(value) => { + self.compile_expression(value)?; + match cause { + Some(cause) => { + self.compile_expression(cause)?; + bytecode::RaiseKind::RaiseCause + } + None => bytecode::RaiseKind::Raise, + } + } + None => bytecode::RaiseKind::Reraise, + }; + emit!(self, Instruction::Raise { kind }); + } + Try { + body, + handlers, + orelse, + finalbody, + } => self.compile_try_statement(body, handlers, orelse, finalbody)?, + TryStar { + body, + handlers, + orelse, + finalbody, + } => self.compile_try_star_statement(body, handlers, orelse, finalbody)?, + FunctionDef { + name, + args, + body, + decorator_list, + returns, + .. + } => self.compile_function_def( + name, + args, + body, + decorator_list, + returns.as_deref(), + false, + )?, + AsyncFunctionDef { + name, + args, + body, + decorator_list, + returns, + .. + } => self.compile_function_def( + name, + args, + body, + decorator_list, + returns.as_deref(), + true, + )?, + ClassDef { + name, + body, + bases, + keywords, + decorator_list, + } => self.compile_class_def(name, body, bases, keywords, decorator_list)?, + Assert { test, msg } => { + // if some flag, ignore all assert statements! + if self.opts.optimize == 0 { + let after_block = self.new_block(); + self.compile_jump_if(test, true, after_block)?; + + let assertion_error = self.name("AssertionError"); + emit!(self, Instruction::LoadGlobal(assertion_error)); + match msg { + Some(e) => { + self.compile_expression(e)?; + emit!(self, Instruction::CallFunctionPositional { nargs: 1 }); + } + None => { + emit!(self, Instruction::CallFunctionPositional { nargs: 0 }); + } + } + emit!( + self, + Instruction::Raise { + kind: bytecode::RaiseKind::Raise, + } + ); + + self.switch_to_block(after_block); + } + } + Break => match self.ctx.loop_data { + Some((_, end)) => { + emit!(self, Instruction::Break { target: end }); + } + None => { + return Err(self.error_loc(CodegenErrorType::InvalidBreak, statement.location)); + } + }, + Continue => match self.ctx.loop_data { + Some((start, _)) => { + emit!(self, Instruction::Continue { target: start }); + } + None => { + return Err( + self.error_loc(CodegenErrorType::InvalidContinue, statement.location) + ); + } + }, + Return { value } => { + if !self.ctx.in_func() { + return Err(self.error_loc(CodegenErrorType::InvalidReturn, statement.location)); + } + match value { + Some(v) => { + if self.ctx.func == FunctionContext::AsyncFunction + && self + .current_code_info() + .flags + .contains(bytecode::CodeFlags::IS_GENERATOR) + { + return Err(self.error_loc( + CodegenErrorType::AsyncReturnValue, + statement.location, + )); + } + self.compile_expression(v)?; + } + None => { + self.emit_constant(ConstantData::None); + } + } + + emit!(self, Instruction::ReturnValue); + } + Assign { targets, value, .. } => { + self.compile_expression(value)?; + + for (i, target) in targets.iter().enumerate() { + if i + 1 != targets.len() { + emit!(self, Instruction::Duplicate); + } + self.compile_store(target)?; + } + } + AugAssign { target, op, value } => self.compile_augassign(target, op, value)?, + AnnAssign { + target, + annotation, + value, + .. + } => self.compile_annotated_assign(target, annotation, value.as_deref())?, + Delete { targets } => { + for target in targets { + self.compile_delete(target)?; + } + } + Pass => { + // No need to emit any code here :) + } + } + Ok(()) + } + + fn compile_delete(&mut self, expression: &ast::Expr) -> CompileResult<()> { + match &expression.node { + ast::ExprKind::Name { id, .. } => self.compile_name(id, NameUsage::Delete)?, + ast::ExprKind::Attribute { value, attr, .. } => { + self.check_forbidden_name(attr, NameUsage::Delete)?; + self.compile_expression(value)?; + let idx = self.name(attr); + emit!(self, Instruction::DeleteAttr { idx }); + } + ast::ExprKind::Subscript { value, slice, .. } => { + self.compile_expression(value)?; + self.compile_expression(slice)?; + emit!(self, Instruction::DeleteSubscript); + } + ast::ExprKind::Tuple { elts, .. } | ast::ExprKind::List { elts, .. } => { + for element in elts { + self.compile_delete(element)?; + } + } + ast::ExprKind::BinOp { .. } | ast::ExprKind::UnaryOp { .. } => { + return Err(self.error(CodegenErrorType::Delete("expression"))) + } + _ => return Err(self.error(CodegenErrorType::Delete(expression.node.name()))), + } + Ok(()) + } + + fn enter_function( + &mut self, + name: &str, + args: &ast::Arguments, + ) -> CompileResult { + let have_defaults = !args.defaults.is_empty(); + if have_defaults { + // Construct a tuple: + let size = args.defaults.len().to_u32(); + for element in &args.defaults { + self.compile_expression(element)?; + } + emit!(self, Instruction::BuildTuple { size }); + } + + if !args.kw_defaults.is_empty() { + let required_kw_count = args.kwonlyargs.len().saturating_sub(args.kw_defaults.len()); + for (kw, default) in args.kwonlyargs[required_kw_count..] + .iter() + .zip(&args.kw_defaults) + { + self.emit_constant(ConstantData::Str { + value: kw.node.arg.clone(), + }); + self.compile_expression(default)?; + } + emit!( + self, + Instruction::BuildMap { + size: args.kw_defaults.len().to_u32(), + } + ); + } + + let mut func_flags = bytecode::MakeFunctionFlags::empty(); + if have_defaults { + func_flags |= bytecode::MakeFunctionFlags::DEFAULTS; + } + if !args.kw_defaults.is_empty() { + func_flags |= bytecode::MakeFunctionFlags::KW_ONLY_DEFAULTS; + } + + self.push_output( + bytecode::CodeFlags::NEW_LOCALS | bytecode::CodeFlags::IS_OPTIMIZED, + args.posonlyargs.len().to_u32(), + (args.posonlyargs.len() + args.args.len()).to_u32(), + args.kwonlyargs.len().to_u32(), + name.to_owned(), + ); + + let args_iter = std::iter::empty() + .chain(&args.posonlyargs) + .chain(&args.args) + .chain(&args.kwonlyargs); + for name in args_iter { + self.varname(&name.node.arg)?; + } + + if let Some(name) = args.vararg.as_deref() { + self.current_code_info().flags |= bytecode::CodeFlags::HAS_VARARGS; + self.varname(&name.node.arg)?; + } + if let Some(name) = args.kwarg.as_deref() { + self.current_code_info().flags |= bytecode::CodeFlags::HAS_VARKEYWORDS; + self.varname(&name.node.arg)?; + } + + Ok(func_flags) + } + + fn prepare_decorators(&mut self, decorator_list: &[ast::Expr]) -> CompileResult<()> { + for decorator in decorator_list { + self.compile_expression(decorator)?; + } + Ok(()) + } + + fn apply_decorators(&mut self, decorator_list: &[ast::Expr]) { + // Apply decorators: + for _ in decorator_list { + emit!(self, Instruction::CallFunctionPositional { nargs: 1 }); + } + } + + fn compile_try_statement( + &mut self, + body: &[ast::Stmt], + handlers: &[ast::Excepthandler], + orelse: &[ast::Stmt], + finalbody: &[ast::Stmt], + ) -> CompileResult<()> { + let handler_block = self.new_block(); + let finally_block = self.new_block(); + + // Setup a finally block if we have a finally statement. + if !finalbody.is_empty() { + emit!( + self, + Instruction::SetupFinally { + handler: finally_block, + } + ); + } + + let else_block = self.new_block(); + + // try: + emit!( + self, + Instruction::SetupExcept { + handler: handler_block, + } + ); + self.compile_statements(body)?; + emit!(self, Instruction::PopBlock); + emit!(self, Instruction::Jump { target: else_block }); + + // except handlers: + self.switch_to_block(handler_block); + // Exception is on top of stack now + for handler in handlers { + let ast::ExcepthandlerKind::ExceptHandler { type_, name, body } = &handler.node; + let next_handler = self.new_block(); + + // If we gave a typ, + // check if this handler can handle the exception: + if let Some(exc_type) = type_ { + // Duplicate exception for test: + emit!(self, Instruction::Duplicate); + + // Check exception type: + self.compile_expression(exc_type)?; + emit!( + self, + Instruction::TestOperation { + op: bytecode::TestOperator::ExceptionMatch, + } + ); + + // We cannot handle this exception type: + emit!( + self, + Instruction::JumpIfFalse { + target: next_handler, + } + ); + + // We have a match, store in name (except x as y) + if let Some(alias) = name { + self.store_name(alias)? + } else { + // Drop exception from top of stack: + emit!(self, Instruction::Pop); + } + } else { + // Catch all! + // Drop exception from top of stack: + emit!(self, Instruction::Pop); + } + + // Handler code: + self.compile_statements(body)?; + emit!(self, Instruction::PopException); + + if !finalbody.is_empty() { + emit!(self, Instruction::PopBlock); // pop excepthandler block + // We enter the finally block, without exception. + emit!(self, Instruction::EnterFinally); + } + + emit!( + self, + Instruction::Jump { + target: finally_block, + } + ); + + // Emit a new label for the next handler + self.switch_to_block(next_handler); + } + + // If code flows here, we have an unhandled exception, + // raise the exception again! + emit!( + self, + Instruction::Raise { + kind: bytecode::RaiseKind::Reraise, + } + ); + + // We successfully ran the try block: + // else: + self.switch_to_block(else_block); + self.compile_statements(orelse)?; + + if !finalbody.is_empty() { + emit!(self, Instruction::PopBlock); // pop finally block + + // We enter the finallyhandler block, without return / exception. + emit!(self, Instruction::EnterFinally); + } + + // finally: + self.switch_to_block(finally_block); + if !finalbody.is_empty() { + self.compile_statements(finalbody)?; + emit!(self, Instruction::EndFinally); + } + + Ok(()) + } + + fn compile_try_star_statement( + &mut self, + _body: &[ast::Stmt], + _handlers: &[ast::Excepthandler], + _orelse: &[ast::Stmt], + _finalbody: &[ast::Stmt], + ) -> CompileResult<()> { + Err(self.error(CodegenErrorType::NotImplementedYet)) + } + + fn is_forbidden_arg_name(name: &str) -> bool { + is_forbidden_name(name) + } + + fn compile_function_def( + &mut self, + name: &str, + args: &ast::Arguments, + body: &[ast::Stmt], + decorator_list: &[ast::Expr], + returns: Option<&ast::Expr>, // TODO: use type hint somehow.. + is_async: bool, + ) -> CompileResult<()> { + // Create bytecode for this function: + + self.prepare_decorators(decorator_list)?; + let mut func_flags = self.enter_function(name, args)?; + self.current_code_info() + .flags + .set(bytecode::CodeFlags::IS_COROUTINE, is_async); + + // remember to restore self.ctx.in_loop to the original after the function is compiled + let prev_ctx = self.ctx; + + self.ctx = CompileContext { + loop_data: None, + in_class: prev_ctx.in_class, + func: if is_async { + FunctionContext::AsyncFunction + } else { + FunctionContext::Function + }, + }; + + self.push_qualified_path(name); + let qualified_name = self.qualified_path.join("."); + self.push_qualified_path(""); + + let (doc_str, body) = split_doc(body); + + self.current_code_info() + .constants + .insert_full(ConstantData::None); + + self.compile_statements(body)?; + + // Emit None at end: + match body.last().map(|s| &s.node) { + Some(ast::StmtKind::Return { .. }) => { + // the last instruction is a ReturnValue already, we don't need to emit it + } + _ => { + self.emit_constant(ConstantData::None); + emit!(self, Instruction::ReturnValue); + } + } + + let code = self.pop_code_object(); + self.qualified_path.pop(); + self.qualified_path.pop(); + self.ctx = prev_ctx; + + // Prepare type annotations: + let mut num_annotations = 0; + + // Return annotation: + if let Some(annotation) = returns { + // key: + self.emit_constant(ConstantData::Str { + value: "return".to_owned(), + }); + // value: + self.compile_annotation(annotation)?; + num_annotations += 1; + } + + let args_iter = std::iter::empty() + .chain(&args.posonlyargs) + .chain(&args.args) + .chain(&args.kwonlyargs) + .chain(args.vararg.as_deref()) + .chain(args.kwarg.as_deref()); + for arg in args_iter { + if let Some(annotation) = &arg.node.annotation { + self.emit_constant(ConstantData::Str { + value: self.mangle(&arg.node.arg).into_owned(), + }); + self.compile_annotation(annotation)?; + num_annotations += 1; + } + } + + if num_annotations > 0 { + func_flags |= bytecode::MakeFunctionFlags::ANNOTATIONS; + emit!( + self, + Instruction::BuildMap { + size: num_annotations, + } + ); + } + + if self.build_closure(&code) { + func_flags |= bytecode::MakeFunctionFlags::CLOSURE; + } + + self.emit_constant(ConstantData::Code { + code: Box::new(code), + }); + self.emit_constant(ConstantData::Str { + value: qualified_name, + }); + + // Turn code object into function object: + emit!(self, Instruction::MakeFunction(func_flags)); + + emit!(self, Instruction::Duplicate); + self.load_docstring(doc_str); + emit!(self, Instruction::Rotate2); + let doc = self.name("__doc__"); + emit!(self, Instruction::StoreAttr { idx: doc }); + + self.apply_decorators(decorator_list); + + self.store_name(name) + } + + fn build_closure(&mut self, code: &CodeObject) -> bool { + if code.freevars.is_empty() { + return false; + } + for var in &*code.freevars { + let table = self.symbol_table_stack.last().unwrap(); + let symbol = table.lookup(var).unwrap_or_else(|| { + panic!( + "couldn't look up var {} in {} in {}", + var, code.obj_name, self.source_path + ) + }); + let parent_code = self.code_stack.last().unwrap(); + let vars = match symbol.scope { + SymbolScope::Free => &parent_code.freevar_cache, + SymbolScope::Cell => &parent_code.cellvar_cache, + _ if symbol.flags.contains(SymbolFlags::FREE_CLASS) => &parent_code.freevar_cache, + x => unreachable!( + "var {} in a {:?} should be free or cell but it's {:?}", + var, table.typ, x + ), + }; + let mut idx = vars.get_index_of(var).unwrap(); + if let SymbolScope::Free = symbol.scope { + idx += parent_code.cellvar_cache.len(); + } + emit!(self, Instruction::LoadClosure(idx.to_u32())) + } + emit!( + self, + Instruction::BuildTuple { + size: code.freevars.len().to_u32(), + } + ); + true + } + + // Python/compile.c find_ann + fn find_ann(body: &[ast::Stmt]) -> bool { + use ast::StmtKind::*; + + for statement in body { + let res = match &statement.node { + AnnAssign { .. } => true, + For { body, orelse, .. } => Self::find_ann(body) || Self::find_ann(orelse), + If { body, orelse, .. } => Self::find_ann(body) || Self::find_ann(orelse), + While { body, orelse, .. } => Self::find_ann(body) || Self::find_ann(orelse), + With { body, .. } => Self::find_ann(body), + Try { + body, + orelse, + finalbody, + .. + } => Self::find_ann(body) || Self::find_ann(orelse) || Self::find_ann(finalbody), + _ => false, + }; + if res { + return true; + } + } + false + } + + fn compile_class_def( + &mut self, + name: &str, + body: &[ast::Stmt], + bases: &[ast::Expr], + keywords: &[ast::Keyword], + decorator_list: &[ast::Expr], + ) -> CompileResult<()> { + self.prepare_decorators(decorator_list)?; + + emit!(self, Instruction::LoadBuildClass); + + let prev_ctx = self.ctx; + self.ctx = CompileContext { + func: FunctionContext::NoFunction, + in_class: true, + loop_data: None, + }; + + let prev_class_name = std::mem::replace(&mut self.class_name, Some(name.to_owned())); + + // Check if the class is declared global + let symbol_table = self.symbol_table_stack.last().unwrap(); + let symbol = symbol_table.lookup(name.as_ref()).expect( + "The symbol must be present in the symbol table, even when it is undefined in python.", + ); + let mut global_path_prefix = Vec::new(); + if symbol.scope == SymbolScope::GlobalExplicit { + global_path_prefix.append(&mut self.qualified_path); + } + self.push_qualified_path(name); + let qualified_name = self.qualified_path.join("."); + + self.push_output(bytecode::CodeFlags::empty(), 0, 0, 0, name.to_owned()); + + let (doc_str, body) = split_doc(body); + + let dunder_name = self.name("__name__"); + emit!(self, Instruction::LoadGlobal(dunder_name)); + let dunder_module = self.name("__module__"); + emit!(self, Instruction::StoreLocal(dunder_module)); + self.emit_constant(ConstantData::Str { + value: qualified_name, + }); + let qualname = self.name("__qualname__"); + emit!(self, Instruction::StoreLocal(qualname)); + self.load_docstring(doc_str); + let doc = self.name("__doc__"); + emit!(self, Instruction::StoreLocal(doc)); + // setup annotations + if Self::find_ann(body) { + emit!(self, Instruction::SetupAnnotation); + } + self.compile_statements(body)?; + + let classcell_idx = self + .code_stack + .last_mut() + .unwrap() + .cellvar_cache + .iter() + .position(|var| *var == "__class__"); + + if let Some(classcell_idx) = classcell_idx { + emit!(self, Instruction::LoadClosure(classcell_idx.to_u32())); + emit!(self, Instruction::Duplicate); + let classcell = self.name("__classcell__"); + emit!(self, Instruction::StoreLocal(classcell)); + } else { + self.emit_constant(ConstantData::None); + } + + emit!(self, Instruction::ReturnValue); + + let code = self.pop_code_object(); + + self.class_name = prev_class_name; + self.qualified_path.pop(); + self.qualified_path.append(global_path_prefix.as_mut()); + self.ctx = prev_ctx; + + let mut func_flags = bytecode::MakeFunctionFlags::empty(); + + if self.build_closure(&code) { + func_flags |= bytecode::MakeFunctionFlags::CLOSURE; + } + + self.emit_constant(ConstantData::Code { + code: Box::new(code), + }); + self.emit_constant(ConstantData::Str { + value: name.to_owned(), + }); + + // Turn code object into function object: + emit!(self, Instruction::MakeFunction(func_flags)); + + self.emit_constant(ConstantData::Str { + value: name.to_owned(), + }); + + let call = self.compile_call_inner(2, bases, keywords)?; + self.compile_normal_call(call); + + self.apply_decorators(decorator_list); + + self.store_name(name) + } + + fn load_docstring(&mut self, doc_str: Option) { + // TODO: __doc__ must be default None and no bytecode unless it is Some + // Duplicate top of stack (the function or class object) + + // Doc string value: + self.emit_constant(match doc_str { + Some(doc) => ConstantData::Str { value: doc }, + None => ConstantData::None, // set docstring None if not declared + }); + } + + fn compile_while( + &mut self, + test: &ast::Expr, + body: &[ast::Stmt], + orelse: &[ast::Stmt], + ) -> CompileResult<()> { + let while_block = self.new_block(); + let else_block = self.new_block(); + let after_block = self.new_block(); + + emit!(self, Instruction::SetupLoop); + self.switch_to_block(while_block); + + self.compile_jump_if(test, false, else_block)?; + + let was_in_loop = self.ctx.loop_data.replace((while_block, after_block)); + self.compile_statements(body)?; + self.ctx.loop_data = was_in_loop; + emit!( + self, + Instruction::Jump { + target: while_block, + } + ); + self.switch_to_block(else_block); + emit!(self, Instruction::PopBlock); + self.compile_statements(orelse)?; + self.switch_to_block(after_block); + Ok(()) + } + + fn compile_with( + &mut self, + items: &[ast::Withitem], + body: &[ast::Stmt], + is_async: bool, + ) -> CompileResult<()> { + let with_location = self.current_source_location; + + let Some((item, items)) = items.split_first() else { + return Err(self.error(CodegenErrorType::EmptyWithItems)); + }; + + let final_block = { + let final_block = self.new_block(); + self.compile_expression(&item.context_expr)?; + + self.set_source_location(with_location); + if is_async { + emit!(self, Instruction::BeforeAsyncWith); + emit!(self, Instruction::GetAwaitable); + self.emit_constant(ConstantData::None); + emit!(self, Instruction::YieldFrom); + emit!(self, Instruction::SetupAsyncWith { end: final_block }); + } else { + emit!(self, Instruction::SetupWith { end: final_block }); + } + + match &item.optional_vars { + Some(var) => { + self.set_source_location(var.location); + self.compile_store(var)?; + } + None => { + emit!(self, Instruction::Pop); + } + } + final_block + }; + + if items.is_empty() { + if body.is_empty() { + return Err(self.error(CodegenErrorType::EmptyWithBody)); + } + self.compile_statements(body)?; + } else { + self.set_source_location(with_location); + self.compile_with(items, body, is_async)?; + } + + // sort of "stack up" the layers of with blocks: + // with a, b: body -> start_with(a) start_with(b) body() end_with(b) end_with(a) + self.set_source_location(with_location); + emit!(self, Instruction::PopBlock); + + emit!(self, Instruction::EnterFinally); + + self.switch_to_block(final_block); + emit!(self, Instruction::WithCleanupStart); + + if is_async { + emit!(self, Instruction::GetAwaitable); + self.emit_constant(ConstantData::None); + emit!(self, Instruction::YieldFrom); + } + + emit!(self, Instruction::WithCleanupFinish); + + Ok(()) + } + + fn compile_for( + &mut self, + target: &ast::Expr, + iter: &ast::Expr, + body: &[ast::Stmt], + orelse: &[ast::Stmt], + is_async: bool, + ) -> CompileResult<()> { + // Start loop + let for_block = self.new_block(); + let else_block = self.new_block(); + let after_block = self.new_block(); + + emit!(self, Instruction::SetupLoop); + + // The thing iterated: + self.compile_expression(iter)?; + + if is_async { + emit!(self, Instruction::GetAIter); + + self.switch_to_block(for_block); + emit!( + self, + Instruction::SetupExcept { + handler: else_block, + } + ); + emit!(self, Instruction::GetANext); + self.emit_constant(ConstantData::None); + emit!(self, Instruction::YieldFrom); + self.compile_store(target)?; + emit!(self, Instruction::PopBlock); + } else { + // Retrieve Iterator + emit!(self, Instruction::GetIter); + + self.switch_to_block(for_block); + emit!(self, Instruction::ForIter { target: else_block }); + + // Start of loop iteration, set targets: + self.compile_store(target)?; + }; + + let was_in_loop = self.ctx.loop_data.replace((for_block, after_block)); + self.compile_statements(body)?; + self.ctx.loop_data = was_in_loop; + emit!(self, Instruction::Jump { target: for_block }); + + self.switch_to_block(else_block); + if is_async { + emit!(self, Instruction::EndAsyncFor); + } + emit!(self, Instruction::PopBlock); + self.compile_statements(orelse)?; + + self.switch_to_block(after_block); + + Ok(()) + } + + fn compile_match( + &mut self, + subject: &ast::Expr, + cases: &[ast::MatchCase], + ) -> CompileResult<()> { + eprintln!("match subject: {subject:?}"); + eprintln!("match cases: {cases:?}"); + Err(self.error(CodegenErrorType::NotImplementedYet)) + } + + fn compile_chained_comparison( + &mut self, + left: &ast::Expr, + ops: &[ast::Cmpop], + exprs: &[ast::Expr], + ) -> CompileResult<()> { + assert!(!ops.is_empty()); + assert_eq!(exprs.len(), ops.len()); + let (last_op, mid_ops) = ops.split_last().unwrap(); + let (last_val, mid_exprs) = exprs.split_last().unwrap(); + + use bytecode::ComparisonOperator::*; + use bytecode::TestOperator::*; + let compile_cmpop = |c: &mut Self, op: &ast::Cmpop| match op { + ast::Cmpop::Eq => emit!(c, Instruction::CompareOperation { op: Equal }), + ast::Cmpop::NotEq => emit!(c, Instruction::CompareOperation { op: NotEqual }), + ast::Cmpop::Lt => emit!(c, Instruction::CompareOperation { op: Less }), + ast::Cmpop::LtE => emit!(c, Instruction::CompareOperation { op: LessOrEqual }), + ast::Cmpop::Gt => emit!(c, Instruction::CompareOperation { op: Greater }), + ast::Cmpop::GtE => emit!(c, Instruction::CompareOperation { op: GreaterOrEqual }), + ast::Cmpop::In => emit!(c, Instruction::TestOperation { op: In }), + ast::Cmpop::NotIn => emit!(c, Instruction::TestOperation { op: NotIn }), + ast::Cmpop::Is => emit!(c, Instruction::TestOperation { op: Is }), + ast::Cmpop::IsNot => emit!(c, Instruction::TestOperation { op: IsNot }), + }; + + // a == b == c == d + // compile into (pseudo code): + // result = a == b + // if result: + // result = b == c + // if result: + // result = c == d + + // initialize lhs outside of loop + self.compile_expression(left)?; + + let end_blocks = if mid_exprs.is_empty() { + None + } else { + let break_block = self.new_block(); + let after_block = self.new_block(); + Some((break_block, after_block)) + }; + + // for all comparisons except the last (as the last one doesn't need a conditional jump) + for (op, val) in mid_ops.iter().zip(mid_exprs) { + self.compile_expression(val)?; + // store rhs for the next comparison in chain + emit!(self, Instruction::Duplicate); + emit!(self, Instruction::Rotate3); + + compile_cmpop(self, op); + + // if comparison result is false, we break with this value; if true, try the next one. + if let Some((break_block, _)) = end_blocks { + emit!( + self, + Instruction::JumpIfFalseOrPop { + target: break_block, + } + ); + } + } + + // handle the last comparison + self.compile_expression(last_val)?; + compile_cmpop(self, last_op); + + if let Some((break_block, after_block)) = end_blocks { + emit!( + self, + Instruction::Jump { + target: after_block, + } + ); + + // early exit left us with stack: `rhs, comparison_result`. We need to clean up rhs. + self.switch_to_block(break_block); + emit!(self, Instruction::Rotate2); + emit!(self, Instruction::Pop); + + self.switch_to_block(after_block); + } + + Ok(()) + } + + fn compile_annotation(&mut self, annotation: &ast::Expr) -> CompileResult<()> { + if self.future_annotations { + self.emit_constant(ConstantData::Str { + value: annotation.to_string(), + }); + } else { + self.compile_expression(annotation)?; + } + Ok(()) + } + + fn compile_annotated_assign( + &mut self, + target: &ast::Expr, + annotation: &ast::Expr, + value: Option<&ast::Expr>, + ) -> CompileResult<()> { + if let Some(value) = value { + self.compile_expression(value)?; + self.compile_store(target)?; + } + + // Annotations are only evaluated in a module or class. + if self.ctx.in_func() { + return Ok(()); + } + + // Compile annotation: + self.compile_annotation(annotation)?; + + if let ast::ExprKind::Name { id, .. } = &target.node { + // Store as dict entry in __annotations__ dict: + let annotations = self.name("__annotations__"); + emit!(self, Instruction::LoadNameAny(annotations)); + self.emit_constant(ConstantData::Str { + value: self.mangle(id).into_owned(), + }); + emit!(self, Instruction::StoreSubscript); + } else { + // Drop annotation if not assigned to simple identifier. + emit!(self, Instruction::Pop); + } + + Ok(()) + } + + fn compile_store(&mut self, target: &ast::Expr) -> CompileResult<()> { + match &target.node { + ast::ExprKind::Name { id, .. } => self.store_name(id)?, + ast::ExprKind::Subscript { value, slice, .. } => { + self.compile_expression(value)?; + self.compile_expression(slice)?; + emit!(self, Instruction::StoreSubscript); + } + ast::ExprKind::Attribute { value, attr, .. } => { + self.check_forbidden_name(attr, NameUsage::Store)?; + self.compile_expression(value)?; + let idx = self.name(attr); + emit!(self, Instruction::StoreAttr { idx }); + } + ast::ExprKind::List { elts, .. } | ast::ExprKind::Tuple { elts, .. } => { + let mut seen_star = false; + + // Scan for star args: + for (i, element) in elts.iter().enumerate() { + if let ast::ExprKind::Starred { .. } = &element.node { + if seen_star { + return Err(self.error(CodegenErrorType::MultipleStarArgs)); + } else { + seen_star = true; + let before = i; + let after = elts.len() - i - 1; + let (before, after) = (|| Some((before.to_u8()?, after.to_u8()?)))() + .ok_or_else(|| { + self.error_loc( + CodegenErrorType::TooManyStarUnpack, + target.location, + ) + })?; + let args = bytecode::UnpackExArgs { before, after }; + emit!(self, Instruction::UnpackEx { args }); + } + } + } + + if !seen_star { + emit!( + self, + Instruction::UnpackSequence { + size: elts.len().to_u32(), + } + ); + } + + for element in elts { + if let ast::ExprKind::Starred { value, .. } = &element.node { + self.compile_store(value)?; + } else { + self.compile_store(element)?; + } + } + } + _ => { + return Err(self.error(match target.node { + ast::ExprKind::Starred { .. } => CodegenErrorType::SyntaxError( + "starred assignment target must be in a list or tuple".to_owned(), + ), + _ => CodegenErrorType::Assign(target.node.name()), + })); + } + } + + Ok(()) + } + + fn compile_augassign( + &mut self, + target: &ast::Expr, + op: &ast::Operator, + value: &ast::Expr, + ) -> CompileResult<()> { + enum AugAssignKind<'a> { + Name { id: &'a str }, + Subscript, + Attr { idx: bytecode::NameIdx }, + } + + let kind = match &target.node { + ast::ExprKind::Name { id, .. } => { + self.compile_name(id, NameUsage::Load)?; + AugAssignKind::Name { id } + } + ast::ExprKind::Subscript { value, slice, .. } => { + self.compile_expression(value)?; + self.compile_expression(slice)?; + emit!(self, Instruction::Duplicate2); + emit!(self, Instruction::Subscript); + AugAssignKind::Subscript + } + ast::ExprKind::Attribute { value, attr, .. } => { + self.check_forbidden_name(attr, NameUsage::Store)?; + self.compile_expression(value)?; + emit!(self, Instruction::Duplicate); + let idx = self.name(attr); + emit!(self, Instruction::LoadAttr { idx }); + AugAssignKind::Attr { idx } + } + _ => { + return Err(self.error(CodegenErrorType::Assign(target.node.name()))); + } + }; + + self.compile_expression(value)?; + self.compile_op(op, true); + + match kind { + AugAssignKind::Name { id } => { + // stack: RESULT + self.compile_name(id, NameUsage::Store)?; + } + AugAssignKind::Subscript => { + // stack: CONTAINER SLICE RESULT + emit!(self, Instruction::Rotate3); + emit!(self, Instruction::StoreSubscript); + } + AugAssignKind::Attr { idx } => { + // stack: CONTAINER RESULT + emit!(self, Instruction::Rotate2); + emit!(self, Instruction::StoreAttr { idx }); + } + } + + Ok(()) + } + + fn compile_op(&mut self, op: &ast::Operator, inplace: bool) { + let op = match op { + ast::Operator::Add => bytecode::BinaryOperator::Add, + ast::Operator::Sub => bytecode::BinaryOperator::Subtract, + ast::Operator::Mult => bytecode::BinaryOperator::Multiply, + ast::Operator::MatMult => bytecode::BinaryOperator::MatrixMultiply, + ast::Operator::Div => bytecode::BinaryOperator::Divide, + ast::Operator::FloorDiv => bytecode::BinaryOperator::FloorDivide, + ast::Operator::Mod => bytecode::BinaryOperator::Modulo, + ast::Operator::Pow => bytecode::BinaryOperator::Power, + ast::Operator::LShift => bytecode::BinaryOperator::Lshift, + ast::Operator::RShift => bytecode::BinaryOperator::Rshift, + ast::Operator::BitOr => bytecode::BinaryOperator::Or, + ast::Operator::BitXor => bytecode::BinaryOperator::Xor, + ast::Operator::BitAnd => bytecode::BinaryOperator::And, + }; + if inplace { + emit!(self, Instruction::BinaryOperationInplace { op }) + } else { + emit!(self, Instruction::BinaryOperation { op }) + } + } + + /// Implement boolean short circuit evaluation logic. + /// https://en.wikipedia.org/wiki/Short-circuit_evaluation + /// + /// This means, in a boolean statement 'x and y' the variable y will + /// not be evaluated when x is false. + /// + /// The idea is to jump to a label if the expression is either true or false + /// (indicated by the condition parameter). + fn compile_jump_if( + &mut self, + expression: &ast::Expr, + condition: bool, + target_block: ir::BlockIdx, + ) -> CompileResult<()> { + // Compile expression for test, and jump to label if false + match &expression.node { + ast::ExprKind::BoolOp { op, values } => { + match op { + ast::Boolop::And => { + if condition { + // If all values are true. + let end_block = self.new_block(); + let (last_value, values) = values.split_last().unwrap(); + + // If any of the values is false, we can short-circuit. + for value in values { + self.compile_jump_if(value, false, end_block)?; + } + + // It depends upon the last value now: will it be true? + self.compile_jump_if(last_value, true, target_block)?; + self.switch_to_block(end_block); + } else { + // If any value is false, the whole condition is false. + for value in values { + self.compile_jump_if(value, false, target_block)?; + } + } + } + ast::Boolop::Or => { + if condition { + // If any of the values is true. + for value in values { + self.compile_jump_if(value, true, target_block)?; + } + } else { + // If all of the values are false. + let end_block = self.new_block(); + let (last_value, values) = values.split_last().unwrap(); + + // If any value is true, we can short-circuit: + for value in values { + self.compile_jump_if(value, true, end_block)?; + } + + // It all depends upon the last value now! + self.compile_jump_if(last_value, false, target_block)?; + self.switch_to_block(end_block); + } + } + } + } + ast::ExprKind::UnaryOp { + op: ast::Unaryop::Not, + operand, + } => { + self.compile_jump_if(operand, !condition, target_block)?; + } + _ => { + // Fall back case which always will work! + self.compile_expression(expression)?; + if condition { + emit!( + self, + Instruction::JumpIfTrue { + target: target_block, + } + ); + } else { + emit!( + self, + Instruction::JumpIfFalse { + target: target_block, + } + ); + } + } + } + Ok(()) + } + + /// Compile a boolean operation as an expression. + /// This means, that the last value remains on the stack. + fn compile_bool_op(&mut self, op: &ast::Boolop, values: &[ast::Expr]) -> CompileResult<()> { + let after_block = self.new_block(); + + let (last_value, values) = values.split_last().unwrap(); + for value in values { + self.compile_expression(value)?; + + match op { + ast::Boolop::And => { + emit!( + self, + Instruction::JumpIfFalseOrPop { + target: after_block, + } + ); + } + ast::Boolop::Or => { + emit!( + self, + Instruction::JumpIfTrueOrPop { + target: after_block, + } + ); + } + } + } + + // If all values did not qualify, take the value of the last value: + self.compile_expression(last_value)?; + self.switch_to_block(after_block); + Ok(()) + } + + fn compile_dict( + &mut self, + keys: &[Option], + values: &[ast::Expr], + ) -> CompileResult<()> { + let mut size = 0; + let (packed, unpacked): (Vec<_>, Vec<_>) = keys + .iter() + .zip(values.iter()) + .partition(|(k, _)| k.is_some()); + for (key, value) in packed { + self.compile_expression(key.as_ref().unwrap())?; + self.compile_expression(value)?; + size += 1; + } + emit!(self, Instruction::BuildMap { size }); + + for (_, value) in unpacked { + self.compile_expression(value)?; + emit!(self, Instruction::DictUpdate); + } + + Ok(()) + } + + fn compile_expression(&mut self, expression: &ast::Expr) -> CompileResult<()> { + trace!("Compiling {:?}", expression); + self.set_source_location(expression.location); + + use ast::ExprKind::*; + match &expression.node { + Call { + func, + args, + keywords, + } => self.compile_call(func, args, keywords)?, + BoolOp { op, values } => self.compile_bool_op(op, values)?, + BinOp { left, op, right } => { + self.compile_expression(left)?; + self.compile_expression(right)?; + + // Perform operation: + self.compile_op(op, false); + } + Subscript { value, slice, .. } => { + self.compile_expression(value)?; + self.compile_expression(slice)?; + emit!(self, Instruction::Subscript); + } + UnaryOp { op, operand } => { + self.compile_expression(operand)?; + + // Perform operation: + let op = match op { + ast::Unaryop::UAdd => bytecode::UnaryOperator::Plus, + ast::Unaryop::USub => bytecode::UnaryOperator::Minus, + ast::Unaryop::Not => bytecode::UnaryOperator::Not, + ast::Unaryop::Invert => bytecode::UnaryOperator::Invert, + }; + emit!(self, Instruction::UnaryOperation { op }); + } + Attribute { value, attr, .. } => { + self.compile_expression(value)?; + let idx = self.name(attr); + emit!(self, Instruction::LoadAttr { idx }); + } + Compare { + left, + ops, + comparators, + } => { + self.compile_chained_comparison(left, ops, comparators)?; + } + Constant { value, .. } => { + self.emit_constant(compile_constant(value)); + } + List { elts, .. } => { + let (size, unpack) = self.gather_elements(0, elts)?; + if unpack { + emit!(self, Instruction::BuildListUnpack { size }); + } else { + emit!(self, Instruction::BuildList { size }); + } + } + Tuple { elts, .. } => { + let (size, unpack) = self.gather_elements(0, elts)?; + if unpack { + emit!(self, Instruction::BuildTupleUnpack { size }); + } else { + emit!(self, Instruction::BuildTuple { size }); + } + } + Set { elts, .. } => { + let (size, unpack) = self.gather_elements(0, elts)?; + if unpack { + emit!(self, Instruction::BuildSetUnpack { size }); + } else { + emit!(self, Instruction::BuildSet { size }); + } + } + Dict { keys, values } => { + self.compile_dict(keys, values)?; + } + Slice { lower, upper, step } => { + let mut compile_bound = |bound: Option<&ast::Expr>| match bound { + Some(exp) => self.compile_expression(exp), + None => { + self.emit_constant(ConstantData::None); + Ok(()) + } + }; + compile_bound(lower.as_deref())?; + compile_bound(upper.as_deref())?; + if let Some(step) = step { + self.compile_expression(step)?; + } + let step = step.is_some(); + emit!(self, Instruction::BuildSlice { step }); + } + Yield { value } => { + if !self.ctx.in_func() { + return Err(self.error(CodegenErrorType::InvalidYield)); + } + self.mark_generator(); + match value { + Some(expression) => self.compile_expression(expression)?, + Option::None => self.emit_constant(ConstantData::None), + }; + emit!(self, Instruction::YieldValue); + } + Await { value } => { + if self.ctx.func != FunctionContext::AsyncFunction { + return Err(self.error(CodegenErrorType::InvalidAwait)); + } + self.compile_expression(value)?; + emit!(self, Instruction::GetAwaitable); + self.emit_constant(ConstantData::None); + emit!(self, Instruction::YieldFrom); + } + YieldFrom { value } => { + match self.ctx.func { + FunctionContext::NoFunction => { + return Err(self.error(CodegenErrorType::InvalidYieldFrom)); + } + FunctionContext::AsyncFunction => { + return Err(self.error(CodegenErrorType::AsyncYieldFrom)); + } + FunctionContext::Function => {} + } + self.mark_generator(); + self.compile_expression(value)?; + emit!(self, Instruction::GetIter); + self.emit_constant(ConstantData::None); + emit!(self, Instruction::YieldFrom); + } + ast::ExprKind::JoinedStr { values } => { + if let Some(value) = try_get_constant_string(values) { + self.emit_constant(ConstantData::Str { value }) + } else { + for value in values { + self.compile_expression(value)?; + } + emit!( + self, + Instruction::BuildString { + size: values.len().to_u32(), + } + ) + } + } + ast::ExprKind::FormattedValue { + value, + conversion, + format_spec, + } => { + match format_spec { + Some(spec) => self.compile_expression(spec)?, + None => self.emit_constant(ConstantData::Str { + value: String::new(), + }), + }; + self.compile_expression(value)?; + emit!( + self, + Instruction::FormatValue { + conversion: bytecode::ConversionFlag::try_from(*conversion) + .expect("invalid conversion flag"), + }, + ); + } + Name { id, .. } => self.load_name(id)?, + Lambda { args, body } => { + let prev_ctx = self.ctx; + + let name = "".to_owned(); + let mut func_flags = self.enter_function(&name, args)?; + + self.ctx = CompileContext { + loop_data: Option::None, + in_class: prev_ctx.in_class, + func: FunctionContext::Function, + }; + + self.current_code_info() + .constants + .insert_full(ConstantData::None); + + self.compile_expression(body)?; + emit!(self, Instruction::ReturnValue); + let code = self.pop_code_object(); + if self.build_closure(&code) { + func_flags |= bytecode::MakeFunctionFlags::CLOSURE; + } + self.emit_constant(ConstantData::Code { + code: Box::new(code), + }); + self.emit_constant(ConstantData::Str { value: name }); + // Turn code object into function object: + emit!(self, Instruction::MakeFunction(func_flags)); + + self.ctx = prev_ctx; + } + ListComp { elt, generators } => { + self.compile_comprehension( + "", + Some(Instruction::BuildList { + size: OpArgMarker::marker(), + }), + generators, + &|compiler| { + compiler.compile_comprehension_element(elt)?; + emit!( + compiler, + Instruction::ListAppend { + i: generators.len().to_u32(), + } + ); + Ok(()) + }, + )?; + } + SetComp { elt, generators } => { + self.compile_comprehension( + "", + Some(Instruction::BuildSet { + size: OpArgMarker::marker(), + }), + generators, + &|compiler| { + compiler.compile_comprehension_element(elt)?; + emit!( + compiler, + Instruction::SetAdd { + i: generators.len().to_u32(), + } + ); + Ok(()) + }, + )?; + } + DictComp { + key, + value, + generators, + } => { + self.compile_comprehension( + "", + Some(Instruction::BuildMap { + size: OpArgMarker::marker(), + }), + generators, + &|compiler| { + // changed evaluation order for Py38 named expression PEP 572 + compiler.compile_expression(key)?; + compiler.compile_expression(value)?; + + emit!( + compiler, + Instruction::MapAdd { + i: generators.len().to_u32(), + } + ); + + Ok(()) + }, + )?; + } + GeneratorExp { elt, generators } => { + self.compile_comprehension("", None, generators, &|compiler| { + compiler.compile_comprehension_element(elt)?; + compiler.mark_generator(); + emit!(compiler, Instruction::YieldValue); + emit!(compiler, Instruction::Pop); + + Ok(()) + })?; + } + Starred { .. } => { + return Err(self.error(CodegenErrorType::InvalidStarExpr)); + } + IfExp { test, body, orelse } => { + let else_block = self.new_block(); + let after_block = self.new_block(); + self.compile_jump_if(test, false, else_block)?; + + // True case + self.compile_expression(body)?; + emit!( + self, + Instruction::Jump { + target: after_block, + } + ); + + // False case + self.switch_to_block(else_block); + self.compile_expression(orelse)?; + + // End + self.switch_to_block(after_block); + } + + NamedExpr { target, value } => { + self.compile_expression(value)?; + emit!(self, Instruction::Duplicate); + self.compile_store(target)?; + } + } + Ok(()) + } + + fn compile_keywords(&mut self, keywords: &[ast::Keyword]) -> CompileResult<()> { + let mut size = 0; + let groupby = keywords.iter().group_by(|e| e.node.arg.is_none()); + for (is_unpacking, sub_keywords) in &groupby { + if is_unpacking { + for keyword in sub_keywords { + self.compile_expression(&keyword.node.value)?; + size += 1; + } + } else { + let mut sub_size = 0; + for keyword in sub_keywords { + if let Some(name) = &keyword.node.arg { + self.emit_constant(ConstantData::Str { + value: name.to_owned(), + }); + self.compile_expression(&keyword.node.value)?; + sub_size += 1; + } + } + emit!(self, Instruction::BuildMap { size: sub_size }); + size += 1; + } + } + if size > 1 { + emit!(self, Instruction::BuildMapForCall { size }); + } + Ok(()) + } + + fn compile_call( + &mut self, + func: &ast::Expr, + args: &[ast::Expr], + keywords: &[ast::Keyword], + ) -> CompileResult<()> { + let method = if let ast::ExprKind::Attribute { value, attr, .. } = &func.node { + self.compile_expression(value)?; + let idx = self.name(attr); + emit!(self, Instruction::LoadMethod { idx }); + true + } else { + self.compile_expression(func)?; + false + }; + let call = self.compile_call_inner(0, args, keywords)?; + if method { + self.compile_method_call(call) + } else { + self.compile_normal_call(call) + } + Ok(()) + } + + fn compile_normal_call(&mut self, ty: CallType) { + match ty { + CallType::Positional { nargs } => { + emit!(self, Instruction::CallFunctionPositional { nargs }) + } + CallType::Keyword { nargs } => emit!(self, Instruction::CallFunctionKeyword { nargs }), + CallType::Ex { has_kwargs } => emit!(self, Instruction::CallFunctionEx { has_kwargs }), + } + } + fn compile_method_call(&mut self, ty: CallType) { + match ty { + CallType::Positional { nargs } => { + emit!(self, Instruction::CallMethodPositional { nargs }) + } + CallType::Keyword { nargs } => emit!(self, Instruction::CallMethodKeyword { nargs }), + CallType::Ex { has_kwargs } => emit!(self, Instruction::CallMethodEx { has_kwargs }), + } + } + + fn compile_call_inner( + &mut self, + additional_positional: u32, + args: &[ast::Expr], + keywords: &[ast::Keyword], + ) -> CompileResult { + let count = (args.len() + keywords.len()).to_u32() + additional_positional; + + // Normal arguments: + let (size, unpack) = self.gather_elements(additional_positional, args)?; + let has_double_star = keywords.iter().any(|k| k.node.arg.is_none()); + + for keyword in keywords { + if let Some(name) = &keyword.node.arg { + self.check_forbidden_name(name, NameUsage::Store)?; + } + } + + let call = if unpack || has_double_star { + // Create a tuple with positional args: + if unpack { + emit!(self, Instruction::BuildTupleUnpack { size }); + } else { + emit!(self, Instruction::BuildTuple { size }); + } + + // Create an optional map with kw-args: + let has_kwargs = !keywords.is_empty(); + if has_kwargs { + self.compile_keywords(keywords)?; + } + CallType::Ex { has_kwargs } + } else if !keywords.is_empty() { + let mut kwarg_names = vec![]; + for keyword in keywords { + if let Some(name) = &keyword.node.arg { + kwarg_names.push(ConstantData::Str { + value: name.to_owned(), + }); + } else { + // This means **kwargs! + panic!("name must be set"); + } + self.compile_expression(&keyword.node.value)?; + } + + self.emit_constant(ConstantData::Tuple { + elements: kwarg_names, + }); + CallType::Keyword { nargs: count } + } else { + CallType::Positional { nargs: count } + }; + + Ok(call) + } + + // Given a vector of expr / star expr generate code which gives either + // a list of expressions on the stack, or a list of tuples. + fn gather_elements( + &mut self, + before: u32, + elements: &[ast::Expr], + ) -> CompileResult<(u32, bool)> { + // First determine if we have starred elements: + let has_stars = elements + .iter() + .any(|e| matches!(e.node, ast::ExprKind::Starred { .. })); + + let size = if has_stars { + let mut size = 0; + + if before > 0 { + emit!(self, Instruction::BuildTuple { size: before }); + size += 1; + } + + let groups = elements + .iter() + .map(|element| { + if let ast::ExprKind::Starred { value, .. } = &element.node { + (true, value.as_ref()) + } else { + (false, element) + } + }) + .group_by(|(starred, _)| *starred); + + for (starred, run) in &groups { + let mut run_size = 0; + for (_, value) in run { + self.compile_expression(value)?; + run_size += 1 + } + if starred { + size += run_size + } else { + emit!(self, Instruction::BuildTuple { size: run_size }); + size += 1 + } + } + + size + } else { + for element in elements { + self.compile_expression(element)?; + } + before + elements.len().to_u32() + }; + + Ok((size, has_stars)) + } + + fn compile_comprehension_element(&mut self, element: &ast::Expr) -> CompileResult<()> { + self.compile_expression(element).map_err(|e| { + if let CodegenErrorType::InvalidStarExpr = e.error { + self.error(CodegenErrorType::SyntaxError( + "iterable unpacking cannot be used in comprehension".to_owned(), + )) + } else { + e + } + }) + } + + fn compile_comprehension( + &mut self, + name: &str, + init_collection: Option, + generators: &[ast::Comprehension], + compile_element: &dyn Fn(&mut Self) -> CompileResult<()>, + ) -> CompileResult<()> { + let prev_ctx = self.ctx; + + self.ctx = CompileContext { + loop_data: None, + in_class: prev_ctx.in_class, + func: FunctionContext::Function, + }; + + // We must have at least one generator: + assert!(!generators.is_empty()); + + // Create magnificent function : + self.push_output( + bytecode::CodeFlags::NEW_LOCALS | bytecode::CodeFlags::IS_OPTIMIZED, + 1, + 1, + 0, + name.to_owned(), + ); + let arg0 = self.varname(".0")?; + + let return_none = init_collection.is_none(); + // Create empty object of proper type: + if let Some(init_collection) = init_collection { + self._emit(init_collection, OpArg(0), ir::BlockIdx::NULL) + } + + let mut loop_labels = vec![]; + for generator in generators { + if generator.is_async > 0 { + unimplemented!("async for comprehensions"); + } + + let loop_block = self.new_block(); + let after_block = self.new_block(); + + if loop_labels.is_empty() { + // Load iterator onto stack (passed as first argument): + emit!(self, Instruction::LoadFast(arg0)); + } else { + // Evaluate iterated item: + self.compile_expression(&generator.iter)?; + + // Get iterator / turn item into an iterator + emit!(self, Instruction::GetIter); + } + + loop_labels.push((loop_block, after_block)); + + self.switch_to_block(loop_block); + emit!( + self, + Instruction::ForIter { + target: after_block, + } + ); + + self.compile_store(&generator.target)?; + + // Now evaluate the ifs: + for if_condition in &generator.ifs { + self.compile_jump_if(if_condition, false, loop_block)? + } + } + + compile_element(self)?; + + for (loop_block, after_block) in loop_labels.iter().rev().copied() { + // Repeat: + emit!(self, Instruction::Jump { target: loop_block }); + + // End of for loop: + self.switch_to_block(after_block); + } + + if return_none { + self.emit_constant(ConstantData::None) + } + + // Return freshly filled list: + emit!(self, Instruction::ReturnValue); + + // Fetch code for listcomp function: + let code = self.pop_code_object(); + + self.ctx = prev_ctx; + + let mut func_flags = bytecode::MakeFunctionFlags::empty(); + if self.build_closure(&code) { + func_flags |= bytecode::MakeFunctionFlags::CLOSURE; + } + + // List comprehension code: + self.emit_constant(ConstantData::Code { + code: Box::new(code), + }); + + // List comprehension function name: + self.emit_constant(ConstantData::Str { + value: name.to_owned(), + }); + + // Turn code object into function object: + emit!(self, Instruction::MakeFunction(func_flags)); + + // Evaluate iterated item: + self.compile_expression(&generators[0].iter)?; + + // Get iterator / turn item into an iterator + emit!(self, Instruction::GetIter); + + // Call just created function: + emit!(self, Instruction::CallFunctionPositional { nargs: 1 }); + Ok(()) + } + + fn compile_future_features(&mut self, features: &[ast::Alias]) -> Result<(), CodegenError> { + if self.done_with_future_stmts { + return Err(self.error(CodegenErrorType::InvalidFuturePlacement)); + } + for feature in features { + match &*feature.node.name { + // Python 3 features; we've already implemented them by default + "nested_scopes" | "generators" | "division" | "absolute_import" + | "with_statement" | "print_function" | "unicode_literals" => {} + // "generator_stop" => {} + "annotations" => self.future_annotations = true, + other => { + return Err(self.error(CodegenErrorType::InvalidFutureFeature(other.to_owned()))) + } + } + } + Ok(()) + } + + // Low level helper functions: + fn _emit(&mut self, instr: Instruction, arg: OpArg, target: ir::BlockIdx) { + let location = compile_location(&self.current_source_location); + // TODO: insert source filename + self.current_block().instructions.push(ir::InstructionInfo { + instr, + arg, + target, + location, + }); + } + + fn emit_no_arg(&mut self, ins: Instruction) { + self._emit(ins, OpArg::null(), ir::BlockIdx::NULL) + } + + fn emit_arg>( + &mut self, + arg: T, + f: impl FnOnce(OpArgMarker) -> Instruction, + ) { + let (op, arg, target) = arg.emit(f); + self._emit(op, arg, target) + } + + // fn block_done() + + fn emit_constant(&mut self, constant: ConstantData) { + let info = self.current_code_info(); + let idx = info.constants.insert_full(constant).0.to_u32(); + self.emit_arg(idx, |idx| Instruction::LoadConst { idx }) + } + + fn current_code_info(&mut self) -> &mut ir::CodeInfo { + self.code_stack.last_mut().expect("no code on stack") + } + + fn current_block(&mut self) -> &mut ir::Block { + let info = self.current_code_info(); + &mut info.blocks[info.current_block] + } + + fn new_block(&mut self) -> ir::BlockIdx { + let code = self.current_code_info(); + let idx = ir::BlockIdx(code.blocks.len().to_u32()); + code.blocks.push(ir::Block::default()); + idx + } + + fn switch_to_block(&mut self, block: ir::BlockIdx) { + let code = self.current_code_info(); + let prev = code.current_block; + assert_eq!( + code.blocks[block].next, + ir::BlockIdx::NULL, + "switching to completed block" + ); + let prev_block = &mut code.blocks[prev.0 as usize]; + assert_eq!( + prev_block.next.0, + u32::MAX, + "switching from block that's already got a next" + ); + prev_block.next = block; + code.current_block = block; + } + + fn set_source_location(&mut self, location: Location) { + self.current_source_location = location; + } + + fn get_source_line_number(&self) -> u32 { + self.current_source_location.row().to_u32() + } + + fn push_qualified_path(&mut self, name: &str) { + self.qualified_path.push(name.to_owned()); + } + + fn mark_generator(&mut self) { + self.current_code_info().flags |= bytecode::CodeFlags::IS_GENERATOR + } +} + +trait EmitArg { + fn emit( + self, + f: impl FnOnce(OpArgMarker) -> Instruction, + ) -> (Instruction, OpArg, ir::BlockIdx); +} +impl EmitArg for T { + fn emit( + self, + f: impl FnOnce(OpArgMarker) -> Instruction, + ) -> (Instruction, OpArg, ir::BlockIdx) { + let (marker, arg) = OpArgMarker::new(self); + (f(marker), arg, ir::BlockIdx::NULL) + } +} +impl EmitArg for ir::BlockIdx { + fn emit( + self, + f: impl FnOnce(OpArgMarker) -> Instruction, + ) -> (Instruction, OpArg, ir::BlockIdx) { + (f(OpArgMarker::marker()), OpArg::null(), self) + } +} + +fn split_doc(body: &[ast::Stmt]) -> (Option, &[ast::Stmt]) { + if let Some((val, body_rest)) = body.split_first() { + if let ast::StmtKind::Expr { value } = &val.node { + if let Some(doc) = try_get_constant_string(std::slice::from_ref(value)) { + return (Some(doc), body_rest); + } + } + } + (None, body) +} + +fn try_get_constant_string(values: &[ast::Expr]) -> Option { + fn get_constant_string_inner(out_string: &mut String, value: &ast::Expr) -> bool { + match &value.node { + ast::ExprKind::Constant { + value: ast::Constant::Str(s), + .. + } => { + out_string.push_str(s); + true + } + ast::ExprKind::JoinedStr { values } => values + .iter() + .all(|value| get_constant_string_inner(out_string, value)), + _ => false, + } + } + let mut out_string = String::new(); + if values + .iter() + .all(|v| get_constant_string_inner(&mut out_string, v)) + { + Some(out_string) + } else { + None + } +} + +fn compile_location(location: &Location) -> bytecode::Location { + bytecode::Location::new(location.row(), location.column()) +} + +fn compile_constant(value: &ast::Constant) -> ConstantData { + match value { + ast::Constant::None => ConstantData::None, + ast::Constant::Bool(b) => ConstantData::Boolean { value: *b }, + ast::Constant::Str(s) => ConstantData::Str { value: s.clone() }, + ast::Constant::Bytes(b) => ConstantData::Bytes { value: b.clone() }, + ast::Constant::Int(i) => ConstantData::Integer { value: i.clone() }, + ast::Constant::Tuple(t) => ConstantData::Tuple { + elements: t.iter().map(compile_constant).collect(), + }, + ast::Constant::Float(f) => ConstantData::Float { value: *f }, + ast::Constant::Complex { real, imag } => ConstantData::Complex { + value: Complex64::new(*real, *imag), + }, + ast::Constant::Ellipsis => ConstantData::Ellipsis, + } +} + +// Note: Not a good practice in general. Keep this trait private only for compiler +trait ToU32 { + fn to_u32(self) -> u32; +} + +impl ToU32 for usize { + fn to_u32(self) -> u32 { + self.try_into().unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rustpython_parser as parser; + + fn compile_exec(source: &str) -> CodeObject { + let mut compiler: Compiler = Compiler::new( + CompileOpts::default(), + "source_path".to_owned(), + "".to_owned(), + ); + let ast = parser::parse_program(source, "").unwrap(); + let symbol_scope = SymbolTable::scan_program(&ast).unwrap(); + compiler.compile_program(&ast, symbol_scope).unwrap(); + compiler.pop_code_object() + } + + macro_rules! assert_dis_snapshot { + ($value:expr) => { + insta::assert_snapshot!( + insta::internals::AutoName, + $value.display_expand_code_objects().to_string(), + stringify!($value) + ) + }; + } + + #[test] + fn test_if_ors() { + assert_dis_snapshot!(compile_exec( + "\ +if True or False or False: + pass +" + )); + } + + #[test] + fn test_if_ands() { + assert_dis_snapshot!(compile_exec( + "\ +if True and False and False: + pass +" + )); + } + + #[test] + fn test_if_mixed() { + assert_dis_snapshot!(compile_exec( + "\ +if (True and False) or (False and True): + pass +" + )); + } + + #[test] + fn test_nested_double_async_with() { + assert_dis_snapshot!(compile_exec( + "\ +for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')): + with self.subTest(type=type(stop_exc)): + try: + async with egg(): + raise stop_exc + except Exception as ex: + self.assertIs(ex, stop_exc) + else: + self.fail(f'{stop_exc} was suppressed') +" + )); + } +} diff --git a/codegen/src/error.rs b/codegen/src/error.rs new file mode 100644 index 00000000..e0380a62 --- /dev/null +++ b/codegen/src/error.rs @@ -0,0 +1,83 @@ +use std::fmt; + +pub type CodegenError = rustpython_compiler_core::BaseError; + +#[derive(Debug)] +#[non_exhaustive] +pub enum CodegenErrorType { + /// Invalid assignment, cannot store value in target. + Assign(&'static str), + /// Invalid delete + Delete(&'static str), + SyntaxError(String), + /// Multiple `*` detected + MultipleStarArgs, + /// Misplaced `*` expression + InvalidStarExpr, + /// Break statement outside of loop. + InvalidBreak, + /// Continue statement outside of loop. + InvalidContinue, + InvalidReturn, + InvalidYield, + InvalidYieldFrom, + InvalidAwait, + AsyncYieldFrom, + AsyncReturnValue, + InvalidFuturePlacement, + InvalidFutureFeature(String), + FunctionImportStar, + TooManyStarUnpack, + EmptyWithItems, + EmptyWithBody, + NotImplementedYet, // RustPython marker for unimplemented features +} + +impl std::error::Error for CodegenErrorType {} + +impl fmt::Display for CodegenErrorType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use CodegenErrorType::*; + match self { + Assign(target) => write!(f, "cannot assign to {target}"), + Delete(target) => write!(f, "cannot delete {target}"), + SyntaxError(err) => write!(f, "{}", err.as_str()), + MultipleStarArgs => { + write!(f, "two starred expressions in assignment") + } + InvalidStarExpr => write!(f, "cannot use starred expression here"), + InvalidBreak => write!(f, "'break' outside loop"), + InvalidContinue => write!(f, "'continue' outside loop"), + InvalidReturn => write!(f, "'return' outside function"), + InvalidYield => write!(f, "'yield' outside function"), + InvalidYieldFrom => write!(f, "'yield from' outside function"), + InvalidAwait => write!(f, "'await' outside async function"), + AsyncYieldFrom => write!(f, "'yield from' inside async function"), + AsyncReturnValue => { + write!(f, "'return' with value inside async generator") + } + InvalidFuturePlacement => write!( + f, + "from __future__ imports must occur at the beginning of the file" + ), + InvalidFutureFeature(feat) => { + write!(f, "future feature {feat} is not defined") + } + FunctionImportStar => { + write!(f, "import * only allowed at module level") + } + TooManyStarUnpack => { + write!(f, "too many expressions in star-unpacking assignment") + } + EmptyWithItems => { + write!(f, "empty items on With") + } + EmptyWithBody => { + write!(f, "empty body on With") + } + NotImplementedYet => { + write!(f, "RustPython does not implement this feature yet") + } + } + } +} diff --git a/codegen/src/ir.rs b/codegen/src/ir.rs new file mode 100644 index 00000000..dc7e2e74 --- /dev/null +++ b/codegen/src/ir.rs @@ -0,0 +1,328 @@ +use std::ops; + +use crate::IndexSet; +use rustpython_compiler_core::{ + CodeFlags, CodeObject, CodeUnit, ConstantData, InstrDisplayContext, Instruction, Label, + Location, OpArg, +}; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct BlockIdx(pub u32); +impl BlockIdx { + pub const NULL: BlockIdx = BlockIdx(u32::MAX); + const fn idx(self) -> usize { + self.0 as usize + } +} +impl ops::Index for [Block] { + type Output = Block; + fn index(&self, idx: BlockIdx) -> &Block { + &self[idx.idx()] + } +} +impl ops::IndexMut for [Block] { + fn index_mut(&mut self, idx: BlockIdx) -> &mut Block { + &mut self[idx.idx()] + } +} +impl ops::Index for Vec { + type Output = Block; + fn index(&self, idx: BlockIdx) -> &Block { + &self[idx.idx()] + } +} +impl ops::IndexMut for Vec { + fn index_mut(&mut self, idx: BlockIdx) -> &mut Block { + &mut self[idx.idx()] + } +} + +#[derive(Debug, Copy, Clone)] +pub struct InstructionInfo { + pub instr: Instruction, + pub arg: OpArg, + pub target: BlockIdx, + pub location: Location, +} + +// spell-checker:ignore petgraph +// TODO: look into using petgraph for handling blocks and stuff? it's heavier than this, but it +// might enable more analysis/optimizations +#[derive(Debug)] +pub struct Block { + pub instructions: Vec, + pub next: BlockIdx, +} +impl Default for Block { + fn default() -> Self { + Block { + instructions: Vec::new(), + next: BlockIdx::NULL, + } + } +} + +pub struct CodeInfo { + pub flags: CodeFlags, + pub posonlyarg_count: u32, // Number of positional-only arguments + pub arg_count: u32, + pub kwonlyarg_count: u32, + pub source_path: String, + pub first_line_number: u32, + pub obj_name: String, // Name of the object that created this code object + + pub blocks: Vec, + pub current_block: BlockIdx, + pub constants: IndexSet, + pub name_cache: IndexSet, + pub varname_cache: IndexSet, + pub cellvar_cache: IndexSet, + pub freevar_cache: IndexSet, +} +impl CodeInfo { + pub fn finalize_code(mut self, optimize: u8) -> CodeObject { + if optimize > 0 { + self.dce(); + } + + let max_stackdepth = self.max_stackdepth(); + let cell2arg = self.cell2arg(); + + let CodeInfo { + flags, + posonlyarg_count, + arg_count, + kwonlyarg_count, + source_path, + first_line_number, + obj_name, + + mut blocks, + current_block: _, + constants, + name_cache, + varname_cache, + cellvar_cache, + freevar_cache, + } = self; + + let mut instructions = Vec::new(); + let mut locations = Vec::new(); + + let mut block_to_offset = vec![Label(0); blocks.len()]; + loop { + let mut num_instructions = 0; + for (idx, block) in iter_blocks(&blocks) { + block_to_offset[idx.idx()] = Label(num_instructions as u32); + for instr in &block.instructions { + num_instructions += instr.arg.instr_size() + } + } + + instructions.reserve_exact(num_instructions); + locations.reserve_exact(num_instructions); + + let mut recompile_extended_arg = false; + let mut next_block = BlockIdx(0); + while next_block != BlockIdx::NULL { + let block = &mut blocks[next_block]; + for info in &mut block.instructions { + let (op, arg, target) = (info.instr, &mut info.arg, info.target); + if target != BlockIdx::NULL { + let new_arg = OpArg(block_to_offset[target.idx()].0); + recompile_extended_arg |= new_arg.instr_size() != arg.instr_size(); + *arg = new_arg; + } + let (extras, lo_arg) = arg.split(); + locations.extend(std::iter::repeat(info.location).take(arg.instr_size())); + instructions.extend( + extras + .map(|byte| CodeUnit::new(Instruction::ExtendedArg, byte)) + .chain([CodeUnit { op, arg: lo_arg }]), + ); + } + next_block = block.next; + } + + if !recompile_extended_arg { + break; + } + + instructions.clear(); + locations.clear() + } + + CodeObject { + flags, + posonlyarg_count, + arg_count, + kwonlyarg_count, + source_path, + first_line_number, + obj_name, + + max_stackdepth, + instructions: instructions.into_boxed_slice(), + locations: locations.into_boxed_slice(), + constants: constants.into_iter().collect(), + names: name_cache.into_iter().collect(), + varnames: varname_cache.into_iter().collect(), + cellvars: cellvar_cache.into_iter().collect(), + freevars: freevar_cache.into_iter().collect(), + cell2arg, + } + } + + fn cell2arg(&self) -> Option> { + if self.cellvar_cache.is_empty() { + return None; + } + + let total_args = self.arg_count + + self.kwonlyarg_count + + self.flags.contains(CodeFlags::HAS_VARARGS) as u32 + + self.flags.contains(CodeFlags::HAS_VARKEYWORDS) as u32; + + let mut found_cellarg = false; + let cell2arg = self + .cellvar_cache + .iter() + .map(|var| { + self.varname_cache + .get_index_of(var) + // check that it's actually an arg + .filter(|i| *i < total_args as usize) + .map_or(-1, |i| { + found_cellarg = true; + i as i32 + }) + }) + .collect::>(); + + if found_cellarg { + Some(cell2arg) + } else { + None + } + } + + fn dce(&mut self) { + for block in &mut self.blocks { + let mut last_instr = None; + for (i, ins) in block.instructions.iter().enumerate() { + if ins.instr.unconditional_branch() { + last_instr = Some(i); + break; + } + } + if let Some(i) = last_instr { + block.instructions.truncate(i + 1); + } + } + } + + fn max_stackdepth(&self) -> u32 { + let mut maxdepth = 0u32; + let mut stack = Vec::with_capacity(self.blocks.len()); + let mut start_depths = vec![u32::MAX; self.blocks.len()]; + start_depths[0] = 0; + stack.push(BlockIdx(0)); + const DEBUG: bool = false; + 'process_blocks: while let Some(block) = stack.pop() { + let mut depth = start_depths[block.idx()]; + if DEBUG { + eprintln!("===BLOCK {}===", block.0); + } + let block = &self.blocks[block]; + for i in &block.instructions { + let instr = &i.instr; + let effect = instr.stack_effect(i.arg, false); + if DEBUG { + let display_arg = if i.target == BlockIdx::NULL { + i.arg + } else { + OpArg(i.target.0) + }; + let instr_display = instr.display(display_arg, self); + eprint!("{instr_display}: {depth} {effect:+} => "); + } + let new_depth = depth.checked_add_signed(effect).unwrap(); + if DEBUG { + eprintln!("{new_depth}"); + } + if new_depth > maxdepth { + maxdepth = new_depth + } + // we don't want to worry about Break/Continue, they use unwinding to jump to + // their targets and as such the stack size is taken care of in frame.rs by setting + // it back to the level it was at when SetupLoop was run + if i.target != BlockIdx::NULL + && !matches!( + instr, + Instruction::Continue { .. } | Instruction::Break { .. } + ) + { + let effect = instr.stack_effect(i.arg, true); + let target_depth = depth.checked_add_signed(effect).unwrap(); + if target_depth > maxdepth { + maxdepth = target_depth + } + stackdepth_push(&mut stack, &mut start_depths, i.target, target_depth); + } + depth = new_depth; + if instr.unconditional_branch() { + continue 'process_blocks; + } + } + stackdepth_push(&mut stack, &mut start_depths, block.next, depth); + } + if DEBUG { + eprintln!("DONE: {maxdepth}"); + } + maxdepth + } +} + +impl InstrDisplayContext for CodeInfo { + type Constant = ConstantData; + fn get_constant(&self, i: usize) -> &ConstantData { + &self.constants[i] + } + fn get_name(&self, i: usize) -> &str { + self.name_cache[i].as_ref() + } + fn get_varname(&self, i: usize) -> &str { + self.varname_cache[i].as_ref() + } + fn get_cell_name(&self, i: usize) -> &str { + self.cellvar_cache + .get_index(i) + .unwrap_or_else(|| &self.freevar_cache[i - self.cellvar_cache.len()]) + .as_ref() + } +} + +fn stackdepth_push( + stack: &mut Vec, + start_depths: &mut [u32], + target: BlockIdx, + depth: u32, +) { + let block_depth = &mut start_depths[target.idx()]; + if *block_depth == u32::MAX || depth > *block_depth { + *block_depth = depth; + stack.push(target); + } +} + +fn iter_blocks(blocks: &[Block]) -> impl Iterator + '_ { + let mut next = BlockIdx(0); + std::iter::from_fn(move || { + if next == BlockIdx::NULL { + return None; + } + let (idx, b) = (next, &blocks[next]); + next = b.next; + Some((idx, b)) + }) +} diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs new file mode 100644 index 00000000..910d015a --- /dev/null +++ b/codegen/src/lib.rs @@ -0,0 +1,16 @@ +//! Compile a Python AST or source code into bytecode consumable by RustPython. +#![doc(html_logo_url = "https://raw.githubusercontent.com/RustPython/RustPython/main/logo.png")] +#![doc(html_root_url = "https://docs.rs/rustpython-compiler/")] + +#[macro_use] +extern crate log; + +type IndexMap = indexmap::IndexMap; +type IndexSet = indexmap::IndexSet; + +pub mod compile; +pub mod error; +pub mod ir; +pub mod symboltable; + +pub use compile::CompileOpts; diff --git a/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap b/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap new file mode 100644 index 00000000..93eb0ea6 --- /dev/null +++ b/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap @@ -0,0 +1,14 @@ +--- +source: compiler/codegen/src/compile.rs +expression: "compile_exec(\"\\\nif True and False and False:\n pass\n\")" +--- + 1 0 LoadConst (True) + 1 JumpIfFalse (6) + 2 LoadConst (False) + 3 JumpIfFalse (6) + 4 LoadConst (False) + 5 JumpIfFalse (6) + + 2 >> 6 LoadConst (None) + 7 ReturnValue + diff --git a/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap b/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap new file mode 100644 index 00000000..9594af69 --- /dev/null +++ b/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap @@ -0,0 +1,16 @@ +--- +source: compiler/codegen/src/compile.rs +expression: "compile_exec(\"\\\nif (True and False) or (False and True):\n pass\n\")" +--- + 1 0 LoadConst (True) + 1 JumpIfFalse (4) + 2 LoadConst (False) + 3 JumpIfTrue (8) + >> 4 LoadConst (False) + 5 JumpIfFalse (8) + 6 LoadConst (True) + 7 JumpIfFalse (8) + + 2 >> 8 LoadConst (None) + 9 ReturnValue + diff --git a/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap b/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap new file mode 100644 index 00000000..dd582b7d --- /dev/null +++ b/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap @@ -0,0 +1,14 @@ +--- +source: compiler/codegen/src/compile.rs +expression: "compile_exec(\"\\\nif True or False or False:\n pass\n\")" +--- + 1 0 LoadConst (True) + 1 JumpIfTrue (6) + 2 LoadConst (False) + 3 JumpIfTrue (6) + 4 LoadConst (False) + 5 JumpIfFalse (6) + + 2 >> 6 LoadConst (None) + 7 ReturnValue + diff --git a/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap b/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap new file mode 100644 index 00000000..dcc6f4c2 --- /dev/null +++ b/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap @@ -0,0 +1,85 @@ +--- +source: compiler/codegen/src/compile.rs +expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with egg():\n raise stop_exc\n except Exception as ex:\n self.assertIs(ex, stop_exc)\n else:\n self.fail(f'{stop_exc} was suppressed')\n\")" +--- + 1 0 SetupLoop + 1 LoadNameAny (0, StopIteration) + 2 LoadConst ("spam") + 3 CallFunctionPositional(1) + 4 LoadNameAny (1, StopAsyncIteration) + 5 LoadConst ("ham") + 6 CallFunctionPositional(1) + 7 BuildTuple (2) + 8 GetIter + >> 9 ForIter (68) + 10 StoreLocal (2, stop_exc) + + 2 11 LoadNameAny (3, self) + 12 LoadMethod (4, subTest) + 13 LoadNameAny (5, type) + 14 LoadNameAny (2, stop_exc) + 15 CallFunctionPositional(1) + 16 LoadConst (("type")) + 17 CallMethodKeyword (1) + 18 SetupWith (65) + 19 Pop + + 3 20 SetupExcept (40) + + 4 21 LoadNameAny (6, egg) + 22 CallFunctionPositional(0) + 23 BeforeAsyncWith + 24 GetAwaitable + 25 LoadConst (None) + 26 YieldFrom + 27 SetupAsyncWith (33) + 28 Pop + + 5 29 LoadNameAny (2, stop_exc) + 30 Raise (Raise) + + 4 31 PopBlock + 32 EnterFinally + >> 33 WithCleanupStart + 34 GetAwaitable + 35 LoadConst (None) + 36 YieldFrom + 37 WithCleanupFinish + 38 PopBlock + 39 Jump (54) + >> 40 Duplicate + + 6 41 LoadNameAny (7, Exception) + 42 TestOperation (ExceptionMatch) + 43 JumpIfFalse (53) + 44 StoreLocal (8, ex) + + 7 45 LoadNameAny (3, self) + 46 LoadMethod (9, assertIs) + 47 LoadNameAny (8, ex) + 48 LoadNameAny (2, stop_exc) + 49 CallMethodPositional (2) + 50 Pop + 51 PopException + 52 Jump (63) + >> 53 Raise (Reraise) + + 9 >> 54 LoadNameAny (3, self) + 55 LoadMethod (10, fail) + 56 LoadConst ("") + 57 LoadNameAny (2, stop_exc) + 58 FormatValue (None) + 59 LoadConst (" was suppressed") + 60 BuildString (2) + 61 CallMethodPositional (1) + 62 Pop + + 2 >> 63 PopBlock + 64 EnterFinally + >> 65 WithCleanupStart + 66 WithCleanupFinish + 67 Jump (9) + >> 68 PopBlock + 69 LoadConst (None) + 70 ReturnValue + diff --git a/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap b/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap new file mode 100644 index 00000000..d80f10df --- /dev/null +++ b/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap @@ -0,0 +1,14 @@ +--- +source: compiler/src/compile.rs +expression: "compile_exec(\"\\\nif True and False and False:\n pass\n\")" +--- + 1 0 LoadConst (True) + 1 JumpIfFalse (6) + 2 LoadConst (False) + 3 JumpIfFalse (6) + 4 LoadConst (False) + 5 JumpIfFalse (6) + + 2 >> 6 LoadConst (None) + 7 ReturnValue + diff --git a/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap b/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap new file mode 100644 index 00000000..0a9175bb --- /dev/null +++ b/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap @@ -0,0 +1,16 @@ +--- +source: compiler/src/compile.rs +expression: "compile_exec(\"\\\nif (True and False) or (False and True):\n pass\n\")" +--- + 1 0 LoadConst (True) + 1 JumpIfFalse (4) + 2 LoadConst (False) + 3 JumpIfTrue (8) + >> 4 LoadConst (False) + 5 JumpIfFalse (8) + 6 LoadConst (True) + 7 JumpIfFalse (8) + + 2 >> 8 LoadConst (None) + 9 ReturnValue + diff --git a/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap b/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap new file mode 100644 index 00000000..4b812639 --- /dev/null +++ b/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap @@ -0,0 +1,14 @@ +--- +source: compiler/src/compile.rs +expression: "compile_exec(\"\\\nif True or False or False:\n pass\n\")" +--- + 1 0 LoadConst (True) + 1 JumpIfTrue (6) + 2 LoadConst (False) + 3 JumpIfTrue (6) + 4 LoadConst (False) + 5 JumpIfFalse (6) + + 2 >> 6 LoadConst (None) + 7 ReturnValue + diff --git a/codegen/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap b/codegen/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap new file mode 100644 index 00000000..79a1a86a --- /dev/null +++ b/codegen/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap @@ -0,0 +1,87 @@ +--- +source: compiler/src/compile.rs +expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with woohoo():\n raise stop_exc\n except Exception as ex:\n self.assertIs(ex, stop_exc)\n else:\n self.fail(f'{stop_exc} was suppressed')\n\")" +--- + 1 0 SetupLoop (69) + 1 LoadNameAny (0, StopIteration) + 2 LoadConst ("spam") + 3 CallFunctionPositional (1) + 4 LoadNameAny (1, StopAsyncIteration) + 5 LoadConst ("ham") + 6 CallFunctionPositional (1) + 7 BuildTuple (2, false) + 8 GetIter + >> 9 ForIter (68) + 10 StoreLocal (2, stop_exc) + + 2 11 LoadNameAny (3, self) + 12 LoadMethod (subTest) + 13 LoadNameAny (5, type) + 14 LoadNameAny (2, stop_exc) + 15 CallFunctionPositional (1) + 16 LoadConst (("type")) + 17 CallMethodKeyword (1) + 18 SetupWith (65) + 19 Pop + + 3 20 SetupExcept (40) + + 4 21 LoadNameAny (6, woohoo) + 22 CallFunctionPositional (0) + 23 BeforeAsyncWith + 24 GetAwaitable + 25 LoadConst (None) + 26 YieldFrom + 27 SetupAsyncWith (33) + 28 Pop + + 5 29 LoadNameAny (2, stop_exc) + 30 Raise (Raise) + + 4 31 PopBlock + 32 EnterFinally + >> 33 WithCleanupStart + 34 GetAwaitable + 35 LoadConst (None) + 36 YieldFrom + 37 WithCleanupFinish + 38 PopBlock + 39 Jump (54) + >> 40 Duplicate + + 6 41 LoadNameAny (7, Exception) + 42 TestOperation (ExceptionMatch) + 43 JumpIfFalse (53) + 44 StoreLocal (8, ex) + + 7 45 LoadNameAny (3, self) + 46 LoadMethod (assertIs) + 47 LoadNameAny (8, ex) + 48 LoadNameAny (2, stop_exc) + 49 CallMethodPositional (2) + 50 Pop + 51 PopException + 52 Jump (63) + >> 53 Raise (Reraise) + + 9 >> 54 LoadNameAny (3, self) + 55 LoadMethod (fail) + 56 LoadConst ("") + + 1 57 LoadNameAny (2, stop_exc) + 58 FormatValue (None) + + 9 59 LoadConst (" was suppressed") + 60 BuildString (2) + 61 CallMethodPositional (1) + 62 Pop + + 2 >> 63 PopBlock + 64 EnterFinally + >> 65 WithCleanupStart + 66 WithCleanupFinish + 67 Jump (9) + >> 68 PopBlock + >> 69 LoadConst (None) + 70 ReturnValue + diff --git a/codegen/src/symboltable.rs b/codegen/src/symboltable.rs new file mode 100644 index 00000000..b53e8844 --- /dev/null +++ b/codegen/src/symboltable.rs @@ -0,0 +1,1307 @@ +/* Python code is pre-scanned for symbols in the ast. + +This ensures that global and nonlocal keywords are picked up. +Then the compiler can use the symbol table to generate proper +load and store instructions for names. + +Inspirational file: https://github.com/python/cpython/blob/main/Python/symtable.c +*/ + +use crate::{ + error::{CodegenError, CodegenErrorType}, + IndexMap, +}; +use bitflags::bitflags; +use rustpython_ast as ast; +use rustpython_compiler_core::Location; +use std::{borrow::Cow, fmt}; + +/// Captures all symbols in the current scope, and has a list of sub-scopes in this scope. +#[derive(Clone)] +pub struct SymbolTable { + /// The name of this symbol table. Often the name of the class or function. + pub name: String, + + /// The type of symbol table + pub typ: SymbolTableType, + + /// The line number in the source code where this symboltable begins. + pub line_number: usize, + + // Return True if the block is a nested class or function + pub is_nested: bool, + + /// A set of symbols present on this scope level. + pub symbols: IndexMap, + + /// A list of sub-scopes in the order as found in the + /// AST nodes. + pub sub_tables: Vec, +} + +impl SymbolTable { + fn new(name: String, typ: SymbolTableType, line_number: usize, is_nested: bool) -> Self { + SymbolTable { + name, + typ, + line_number, + is_nested, + symbols: IndexMap::default(), + sub_tables: vec![], + } + } + + pub fn scan_program(program: &[ast::Stmt]) -> SymbolTableResult { + let mut builder = SymbolTableBuilder::new(); + builder.scan_statements(program)?; + builder.finish() + } + + pub fn scan_expr(expr: &ast::Expr) -> SymbolTableResult { + let mut builder = SymbolTableBuilder::new(); + builder.scan_expression(expr, ExpressionContext::Load)?; + builder.finish() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SymbolTableType { + Module, + Class, + Function, + Comprehension, +} + +impl fmt::Display for SymbolTableType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SymbolTableType::Module => write!(f, "module"), + SymbolTableType::Class => write!(f, "class"), + SymbolTableType::Function => write!(f, "function"), + SymbolTableType::Comprehension => write!(f, "comprehension"), + } + } +} + +/// Indicator for a single symbol what the scope of this symbol is. +/// The scope can be unknown, which is unfortunate, but not impossible. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SymbolScope { + Unknown, + Local, + GlobalExplicit, + GlobalImplicit, + Free, + Cell, +} + +bitflags! { + pub struct SymbolFlags: u16 { + const REFERENCED = 0x001; + const ASSIGNED = 0x002; + const PARAMETER = 0x004; + const ANNOTATED = 0x008; + const IMPORTED = 0x010; + const NONLOCAL = 0x020; + // indicates if the symbol gets a value assigned by a named expression in a comprehension + // this is required to correct the scope in the analysis. + const ASSIGNED_IN_COMPREHENSION = 0x040; + // indicates that the symbol is used a bound iterator variable. We distinguish this case + // from normal assignment to detect disallowed re-assignment to iterator variables. + const ITER = 0x080; + /// indicates that the symbol is a free variable in a class method from the scope that the + /// class is defined in, e.g.: + /// ```python + /// def foo(x): + /// class A: + /// def method(self): + /// return x // is_free_class + /// ``` + const FREE_CLASS = 0x100; + const BOUND = Self::ASSIGNED.bits | Self::PARAMETER.bits | Self::IMPORTED.bits | Self::ITER.bits; + } +} + +/// A single symbol in a table. Has various properties such as the scope +/// of the symbol, and also the various uses of the symbol. +#[derive(Debug, Clone)] +pub struct Symbol { + pub name: String, + // pub table: SymbolTableRef, + pub scope: SymbolScope, + pub flags: SymbolFlags, +} + +impl Symbol { + fn new(name: &str) -> Self { + Symbol { + name: name.to_owned(), + // table, + scope: SymbolScope::Unknown, + flags: SymbolFlags::empty(), + } + } + + pub fn is_global(&self) -> bool { + matches!( + self.scope, + SymbolScope::GlobalExplicit | SymbolScope::GlobalImplicit + ) + } + + pub fn is_local(&self) -> bool { + matches!(self.scope, SymbolScope::Local | SymbolScope::Cell) + } + + pub fn is_bound(&self) -> bool { + self.flags.intersects(SymbolFlags::BOUND) + } +} + +#[derive(Debug)] +pub struct SymbolTableError { + error: String, + location: Location, +} + +impl SymbolTableError { + pub fn into_codegen_error(self, source_path: String) -> CodegenError { + CodegenError { + error: CodegenErrorType::SyntaxError(self.error), + location: self.location.with_col_offset(1), + source_path, + } + } +} + +type SymbolTableResult = Result; + +impl SymbolTable { + pub fn lookup(&self, name: &str) -> Option<&Symbol> { + self.symbols.get(name) + } +} + +impl std::fmt::Debug for SymbolTable { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "SymbolTable({:?} symbols, {:?} sub scopes)", + self.symbols.len(), + self.sub_tables.len() + ) + } +} + +/* Perform some sort of analysis on nonlocals, globals etc.. + See also: https://github.com/python/cpython/blob/main/Python/symtable.c#L410 +*/ +fn analyze_symbol_table(symbol_table: &mut SymbolTable) -> SymbolTableResult { + let mut analyzer = SymbolTableAnalyzer::default(); + analyzer.analyze_symbol_table(symbol_table) +} + +type SymbolMap = IndexMap; + +mod stack { + use std::panic; + use std::ptr::NonNull; + pub struct StackStack { + v: Vec>, + } + impl Default for StackStack { + fn default() -> Self { + Self { v: Vec::new() } + } + } + impl StackStack { + /// Appends a reference to this stack for the duration of the function `f`. When `f` + /// returns, the reference will be popped off the stack. + pub fn with_append(&mut self, x: &mut T, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + self.v.push(x.into()); + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| f(self))); + self.v.pop(); + res.unwrap_or_else(|x| panic::resume_unwind(x)) + } + + pub fn iter(&self) -> impl Iterator + DoubleEndedIterator + '_ { + self.as_ref().iter().copied() + } + pub fn iter_mut(&mut self) -> impl Iterator + DoubleEndedIterator + '_ { + self.as_mut().iter_mut().map(|x| &mut **x) + } + // pub fn top(&self) -> Option<&T> { + // self.as_ref().last().copied() + // } + // pub fn top_mut(&mut self) -> Option<&mut T> { + // self.as_mut().last_mut().map(|x| &mut **x) + // } + pub fn len(&self) -> usize { + self.v.len() + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn as_ref(&self) -> &[&T] { + unsafe { &*(self.v.as_slice() as *const [NonNull] as *const [&T]) } + } + + pub fn as_mut(&mut self) -> &mut [&mut T] { + unsafe { &mut *(self.v.as_mut_slice() as *mut [NonNull] as *mut [&mut T]) } + } + } +} +use stack::StackStack; + +/// Symbol table analysis. Can be used to analyze a fully +/// build symbol table structure. It will mark variables +/// as local variables for example. +#[derive(Default)] +#[repr(transparent)] +struct SymbolTableAnalyzer { + tables: StackStack<(SymbolMap, SymbolTableType)>, +} + +impl SymbolTableAnalyzer { + fn analyze_symbol_table(&mut self, symbol_table: &mut SymbolTable) -> SymbolTableResult { + let symbols = std::mem::take(&mut symbol_table.symbols); + let sub_tables = &mut *symbol_table.sub_tables; + + let mut info = (symbols, symbol_table.typ); + self.tables.with_append(&mut info, |list| { + let inner_scope = unsafe { &mut *(list as *mut _ as *mut SymbolTableAnalyzer) }; + // Analyze sub scopes: + for sub_table in sub_tables.iter_mut() { + inner_scope.analyze_symbol_table(sub_table)?; + } + Ok(()) + })?; + + symbol_table.symbols = info.0; + + // Analyze symbols: + for symbol in symbol_table.symbols.values_mut() { + self.analyze_symbol(symbol, symbol_table.typ, sub_tables)?; + } + Ok(()) + } + + fn analyze_symbol( + &mut self, + symbol: &mut Symbol, + st_typ: SymbolTableType, + sub_tables: &mut [SymbolTable], + ) -> SymbolTableResult { + if symbol + .flags + .contains(SymbolFlags::ASSIGNED_IN_COMPREHENSION) + && st_typ == SymbolTableType::Comprehension + { + // propagate symbol to next higher level that can hold it, + // i.e., function or module. Comprehension is skipped and + // Class is not allowed and detected as error. + //symbol.scope = SymbolScope::Nonlocal; + self.analyze_symbol_comprehension(symbol, 0)? + } else { + match symbol.scope { + SymbolScope::Free => { + if !self.tables.as_ref().is_empty() { + let scope_depth = self.tables.as_ref().len(); + // check if the name is already defined in any outer scope + // therefore + if scope_depth < 2 + || self.found_in_outer_scope(&symbol.name) != Some(SymbolScope::Free) + { + return Err(SymbolTableError { + error: format!("no binding for nonlocal '{}' found", symbol.name), + // TODO: accurate location info, somehow + location: Location::default(), + }); + } + } else { + return Err(SymbolTableError { + error: format!( + "nonlocal {} defined at place without an enclosing scope", + symbol.name + ), + // TODO: accurate location info, somehow + location: Location::default(), + }); + } + } + SymbolScope::GlobalExplicit | SymbolScope::GlobalImplicit => { + // TODO: add more checks for globals? + } + SymbolScope::Local | SymbolScope::Cell => { + // all is well + } + SymbolScope::Unknown => { + // Try hard to figure out what the scope of this symbol is. + let scope = if symbol.is_bound() { + self.found_in_inner_scope(sub_tables, &symbol.name, st_typ) + .unwrap_or(SymbolScope::Local) + } else if let Some(scope) = self.found_in_outer_scope(&symbol.name) { + scope + } else if self.tables.is_empty() { + // Don't make assumptions when we don't know. + SymbolScope::Unknown + } else { + // If there are scopes above we assume global. + SymbolScope::GlobalImplicit + }; + symbol.scope = scope; + } + } + } + Ok(()) + } + + fn found_in_outer_scope(&mut self, name: &str) -> Option { + let mut decl_depth = None; + for (i, (symbols, typ)) in self.tables.iter().rev().enumerate() { + if matches!(typ, SymbolTableType::Module) + || matches!(typ, SymbolTableType::Class if name != "__class__") + { + continue; + } + if let Some(sym) = symbols.get(name) { + match sym.scope { + SymbolScope::GlobalExplicit => return Some(SymbolScope::GlobalExplicit), + SymbolScope::GlobalImplicit => {} + _ => { + if sym.is_bound() { + decl_depth = Some(i); + break; + } + } + } + } + } + + if let Some(decl_depth) = decl_depth { + // decl_depth is the number of tables between the current one and + // the one that declared the cell var + for (table, typ) in self.tables.iter_mut().rev().take(decl_depth) { + if let SymbolTableType::Class = typ { + if let Some(free_class) = table.get_mut(name) { + free_class.flags.insert(SymbolFlags::FREE_CLASS) + } else { + let mut symbol = Symbol::new(name); + symbol.flags.insert(SymbolFlags::FREE_CLASS); + symbol.scope = SymbolScope::Free; + table.insert(name.to_owned(), symbol); + } + } else if !table.contains_key(name) { + let mut symbol = Symbol::new(name); + symbol.scope = SymbolScope::Free; + // symbol.is_referenced = true; + table.insert(name.to_owned(), symbol); + } + } + } + + decl_depth.map(|_| SymbolScope::Free) + } + + fn found_in_inner_scope( + &self, + sub_tables: &[SymbolTable], + name: &str, + st_typ: SymbolTableType, + ) -> Option { + sub_tables.iter().find_map(|st| { + let sym = st.symbols.get(name)?; + if sym.scope == SymbolScope::Free || sym.flags.contains(SymbolFlags::FREE_CLASS) { + if st_typ == SymbolTableType::Class && name != "__class__" { + None + } else { + Some(SymbolScope::Cell) + } + } else if sym.scope == SymbolScope::GlobalExplicit && self.tables.is_empty() { + // the symbol is defined on the module level, and an inner scope declares + // a global that points to it + Some(SymbolScope::GlobalExplicit) + } else { + None + } + }) + } + + // Implements the symbol analysis and scope extension for names + // assigned by a named expression in a comprehension. See: + // https://github.com/python/cpython/blob/7b78e7f9fd77bb3280ee39fb74b86772a7d46a70/Python/symtable.c#L1435 + fn analyze_symbol_comprehension( + &mut self, + symbol: &mut Symbol, + parent_offset: usize, + ) -> SymbolTableResult { + // when this is called, we expect to be in the direct parent scope of the scope that contains 'symbol' + let last = self.tables.iter_mut().rev().nth(parent_offset).unwrap(); + let symbols = &mut last.0; + let table_type = last.1; + + // it is not allowed to use an iterator variable as assignee in a named expression + if symbol.flags.contains(SymbolFlags::ITER) { + return Err(SymbolTableError { + error: format!( + "assignment expression cannot rebind comprehension iteration variable {}", + symbol.name + ), + // TODO: accurate location info, somehow + location: Location::default(), + }); + } + + match table_type { + SymbolTableType::Module => { + symbol.scope = SymbolScope::GlobalImplicit; + } + SymbolTableType::Class => { + // named expressions are forbidden in comprehensions on class scope + return Err(SymbolTableError { + error: "assignment expression within a comprehension cannot be used in a class body".to_string(), + // TODO: accurate location info, somehow + location: Location::default(), + }); + } + SymbolTableType::Function => { + if let Some(parent_symbol) = symbols.get_mut(&symbol.name) { + if let SymbolScope::Unknown = parent_symbol.scope { + // this information is new, as the assignment is done in inner scope + parent_symbol.flags.insert(SymbolFlags::ASSIGNED); + } + + symbol.scope = if parent_symbol.is_global() { + parent_symbol.scope + } else { + SymbolScope::Free + }; + } else { + let mut cloned_sym = symbol.clone(); + cloned_sym.scope = SymbolScope::Cell; + last.0.insert(cloned_sym.name.to_owned(), cloned_sym); + } + } + SymbolTableType::Comprehension => { + // TODO check for conflicts - requires more context information about variables + match symbols.get_mut(&symbol.name) { + Some(parent_symbol) => { + // check if assignee is an iterator in top scope + if parent_symbol.flags.contains(SymbolFlags::ITER) { + return Err(SymbolTableError { + error: format!("assignment expression cannot rebind comprehension iteration variable {}", symbol.name), + // TODO: accurate location info, somehow + location: Location::default(), + }); + } + + // we synthesize the assignment to the symbol from inner scope + parent_symbol.flags.insert(SymbolFlags::ASSIGNED); // more checks are required + } + None => { + // extend the scope of the inner symbol + // as we are in a nested comprehension, we expect that the symbol is needed + // outside, too, and set it therefore to non-local scope. I.e., we expect to + // find a definition on a higher level + let mut cloned_sym = symbol.clone(); + cloned_sym.scope = SymbolScope::Free; + last.0.insert(cloned_sym.name.to_owned(), cloned_sym); + } + } + + self.analyze_symbol_comprehension(symbol, parent_offset + 1)?; + } + } + Ok(()) + } +} + +#[derive(Debug, Clone)] +enum SymbolUsage { + Global, + Nonlocal, + Used, + Assigned, + Imported, + AnnotationAssigned, + Parameter, + AnnotationParameter, + AssignedNamedExprInComprehension, + Iter, +} + +struct SymbolTableBuilder { + class_name: Option, + // Scope stack. + tables: Vec, + future_annotations: bool, +} + +/// Enum to indicate in what mode an expression +/// was used. +/// In cpython this is stored in the AST, but I think this +/// is not logical, since it is not context free. +#[derive(Copy, Clone, PartialEq)] +enum ExpressionContext { + Load, + Store, + Delete, + Iter, + IterDefinitionExp, +} + +impl SymbolTableBuilder { + fn new() -> Self { + let mut this = Self { + class_name: None, + tables: vec![], + future_annotations: false, + }; + this.enter_scope("top", SymbolTableType::Module, 0); + this + } + + fn finish(mut self) -> Result { + assert_eq!(self.tables.len(), 1); + let mut symbol_table = self.tables.pop().unwrap(); + analyze_symbol_table(&mut symbol_table)?; + Ok(symbol_table) + } + + fn enter_scope(&mut self, name: &str, typ: SymbolTableType, line_number: usize) { + let is_nested = self + .tables + .last() + .map(|table| table.is_nested || table.typ == SymbolTableType::Function) + .unwrap_or(false); + let table = SymbolTable::new(name.to_owned(), typ, line_number, is_nested); + self.tables.push(table); + } + + /// Pop symbol table and add to sub table of parent table. + fn leave_scope(&mut self) { + let table = self.tables.pop().unwrap(); + self.tables.last_mut().unwrap().sub_tables.push(table); + } + + fn scan_statements(&mut self, statements: &[ast::Stmt]) -> SymbolTableResult { + for statement in statements { + self.scan_statement(statement)?; + } + Ok(()) + } + + fn scan_parameters(&mut self, parameters: &[ast::Arg]) -> SymbolTableResult { + for parameter in parameters { + self.scan_parameter(parameter)?; + } + Ok(()) + } + + fn scan_parameter(&mut self, parameter: &ast::Arg) -> SymbolTableResult { + let usage = if parameter.node.annotation.is_some() { + SymbolUsage::AnnotationParameter + } else { + SymbolUsage::Parameter + }; + self.register_name(¶meter.node.arg, usage, parameter.location) + } + + fn scan_parameters_annotations(&mut self, parameters: &[ast::Arg]) -> SymbolTableResult { + for parameter in parameters { + self.scan_parameter_annotation(parameter)?; + } + Ok(()) + } + + fn scan_parameter_annotation(&mut self, parameter: &ast::Arg) -> SymbolTableResult { + if let Some(annotation) = ¶meter.node.annotation { + self.scan_annotation(annotation)?; + } + Ok(()) + } + + fn scan_annotation(&mut self, annotation: &ast::Expr) -> SymbolTableResult { + if self.future_annotations { + Ok(()) + } else { + self.scan_expression(annotation, ExpressionContext::Load) + } + } + + fn scan_statement(&mut self, statement: &ast::Stmt) -> SymbolTableResult { + use ast::StmtKind::*; + let location = statement.location; + if let ImportFrom { module, names, .. } = &statement.node { + if module.as_deref() == Some("__future__") { + for feature in names { + if feature.node.name == "annotations" { + self.future_annotations = true; + } + } + } + } + match &statement.node { + Global { names } => { + for name in names { + self.register_name(name, SymbolUsage::Global, location)?; + } + } + Nonlocal { names } => { + for name in names { + self.register_name(name, SymbolUsage::Nonlocal, location)?; + } + } + FunctionDef { + name, + body, + args, + decorator_list, + returns, + .. + } + | AsyncFunctionDef { + name, + body, + args, + decorator_list, + returns, + .. + } => { + self.scan_expressions(decorator_list, ExpressionContext::Load)?; + self.register_name(name, SymbolUsage::Assigned, location)?; + if let Some(expression) = returns { + self.scan_annotation(expression)?; + } + self.enter_function(name, args, location.row())?; + self.scan_statements(body)?; + self.leave_scope(); + } + ClassDef { + name, + body, + bases, + keywords, + decorator_list, + } => { + self.enter_scope(name, SymbolTableType::Class, location.row()); + let prev_class = std::mem::replace(&mut self.class_name, Some(name.to_owned())); + self.register_name("__module__", SymbolUsage::Assigned, location)?; + self.register_name("__qualname__", SymbolUsage::Assigned, location)?; + self.register_name("__doc__", SymbolUsage::Assigned, location)?; + self.register_name("__class__", SymbolUsage::Assigned, location)?; + 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.node.value, ExpressionContext::Load)?; + } + self.scan_expressions(decorator_list, ExpressionContext::Load)?; + self.register_name(name, SymbolUsage::Assigned, location)?; + } + Expr { value } => self.scan_expression(value, ExpressionContext::Load)?, + If { test, body, orelse } => { + self.scan_expression(test, ExpressionContext::Load)?; + self.scan_statements(body)?; + self.scan_statements(orelse)?; + } + For { + target, + iter, + body, + orelse, + .. + } + | AsyncFor { + target, + iter, + body, + orelse, + .. + } => { + self.scan_expression(target, ExpressionContext::Store)?; + self.scan_expression(iter, ExpressionContext::Load)?; + self.scan_statements(body)?; + self.scan_statements(orelse)?; + } + While { test, body, orelse } => { + self.scan_expression(test, ExpressionContext::Load)?; + self.scan_statements(body)?; + self.scan_statements(orelse)?; + } + Break | Continue | Pass => { + // No symbols here. + } + Import { names } | ImportFrom { names, .. } => { + for name in names { + if let Some(alias) = &name.node.asname { + // `import my_module as my_alias` + self.register_name(alias, SymbolUsage::Imported, location)?; + } else { + // `import module` + self.register_name( + name.node.name.split('.').next().unwrap(), + SymbolUsage::Imported, + location, + )?; + } + } + } + Return { value } => { + if let Some(expression) = value { + self.scan_expression(expression, ExpressionContext::Load)?; + } + } + Assert { test, msg } => { + self.scan_expression(test, ExpressionContext::Load)?; + if let Some(expression) = msg { + self.scan_expression(expression, ExpressionContext::Load)?; + } + } + Delete { targets } => { + self.scan_expressions(targets, ExpressionContext::Delete)?; + } + Assign { targets, value, .. } => { + self.scan_expressions(targets, ExpressionContext::Store)?; + self.scan_expression(value, ExpressionContext::Load)?; + } + AugAssign { target, value, .. } => { + self.scan_expression(target, ExpressionContext::Store)?; + self.scan_expression(value, ExpressionContext::Load)?; + } + AnnAssign { + target, + annotation, + value, + simple, + } => { + // https://github.com/python/cpython/blob/main/Python/symtable.c#L1233 + match &target.node { + ast::ExprKind::Name { id, .. } if *simple > 0 => { + self.register_name(id, SymbolUsage::AnnotationAssigned, location)?; + } + _ => { + self.scan_expression(target, ExpressionContext::Store)?; + } + } + self.scan_annotation(annotation)?; + if let Some(value) = value { + self.scan_expression(value, ExpressionContext::Load)?; + } + } + With { items, body, .. } | AsyncWith { items, body, .. } => { + for item in items { + self.scan_expression(&item.context_expr, ExpressionContext::Load)?; + if let Some(expression) = &item.optional_vars { + self.scan_expression(expression, ExpressionContext::Store)?; + } + } + self.scan_statements(body)?; + } + Try { + body, + handlers, + orelse, + finalbody, + } + | TryStar { + body, + handlers, + orelse, + finalbody, + } => { + self.scan_statements(body)?; + for handler in handlers { + let ast::ExcepthandlerKind::ExceptHandler { type_, name, body } = &handler.node; + if let Some(expression) = type_ { + self.scan_expression(expression, ExpressionContext::Load)?; + } + if let Some(name) = name { + self.register_name(name, SymbolUsage::Assigned, location)?; + } + self.scan_statements(body)?; + } + self.scan_statements(orelse)?; + self.scan_statements(finalbody)?; + } + Match { + subject: _, + cases: _, + } => { + return Err(SymbolTableError { + error: "match expression is not implemented yet".to_owned(), + location: Location::default(), + }); + } + Raise { exc, cause } => { + if let Some(expression) = exc { + self.scan_expression(expression, ExpressionContext::Load)?; + } + if let Some(expression) = cause { + self.scan_expression(expression, ExpressionContext::Load)?; + } + } + } + Ok(()) + } + + fn scan_expressions( + &mut self, + expressions: &[ast::Expr], + context: ExpressionContext, + ) -> SymbolTableResult { + for expression in expressions { + self.scan_expression(expression, context)?; + } + Ok(()) + } + + fn scan_expression( + &mut self, + expression: &ast::Expr, + context: ExpressionContext, + ) -> SymbolTableResult { + use ast::ExprKind::*; + let location = expression.location; + match &expression.node { + BinOp { left, right, .. } => { + self.scan_expression(left, context)?; + self.scan_expression(right, context)?; + } + BoolOp { values, .. } => { + self.scan_expressions(values, context)?; + } + Compare { + left, comparators, .. + } => { + self.scan_expression(left, context)?; + self.scan_expressions(comparators, context)?; + } + Subscript { value, slice, .. } => { + self.scan_expression(value, ExpressionContext::Load)?; + self.scan_expression(slice, ExpressionContext::Load)?; + } + Attribute { value, .. } => { + self.scan_expression(value, ExpressionContext::Load)?; + } + Dict { keys, values } => { + for (key, value) in keys.iter().zip(values.iter()) { + if let Some(key) = key { + self.scan_expression(key, context)?; + } + self.scan_expression(value, context)?; + } + } + Await { value } => { + self.scan_expression(value, context)?; + } + Yield { value } => { + if let Some(expression) = value { + self.scan_expression(expression, context)?; + } + } + YieldFrom { value } => { + self.scan_expression(value, context)?; + } + UnaryOp { operand, .. } => { + self.scan_expression(operand, context)?; + } + Constant { .. } => {} + Starred { value, .. } => { + self.scan_expression(value, context)?; + } + Tuple { elts, .. } | Set { elts, .. } | List { elts, .. } => { + self.scan_expressions(elts, context)?; + } + Slice { lower, upper, step } => { + if let Some(lower) = lower { + self.scan_expression(lower, context)?; + } + if let Some(upper) = upper { + self.scan_expression(upper, context)?; + } + if let Some(step) = step { + self.scan_expression(step, context)?; + } + } + GeneratorExp { elt, generators } => { + self.scan_comprehension("genexpr", elt, None, generators, location)?; + } + ListComp { elt, generators } => { + self.scan_comprehension("genexpr", elt, None, generators, location)?; + } + SetComp { elt, generators } => { + self.scan_comprehension("genexpr", elt, None, generators, location)?; + } + DictComp { + key, + value, + generators, + } => { + self.scan_comprehension("genexpr", key, Some(value), generators, location)?; + } + Call { + func, + args, + keywords, + } => { + match context { + ExpressionContext::IterDefinitionExp => { + self.scan_expression(func, ExpressionContext::IterDefinitionExp)?; + } + _ => { + self.scan_expression(func, ExpressionContext::Load)?; + } + } + + self.scan_expressions(args, ExpressionContext::Load)?; + for keyword in keywords { + self.scan_expression(&keyword.node.value, ExpressionContext::Load)?; + } + } + FormattedValue { + value, format_spec, .. + } => { + self.scan_expression(value, ExpressionContext::Load)?; + if let Some(spec) = format_spec { + self.scan_expression(spec, ExpressionContext::Load)?; + } + } + JoinedStr { values } => { + for value in values { + self.scan_expression(value, ExpressionContext::Load)?; + } + } + Name { id, .. } => { + // Determine the contextual usage of this symbol: + match context { + ExpressionContext::Delete => { + self.register_name(id, SymbolUsage::Assigned, location)?; + self.register_name(id, SymbolUsage::Used, location)?; + } + ExpressionContext::Load | ExpressionContext::IterDefinitionExp => { + self.register_name(id, SymbolUsage::Used, location)?; + } + ExpressionContext::Store => { + self.register_name(id, SymbolUsage::Assigned, location)?; + } + ExpressionContext::Iter => { + self.register_name(id, SymbolUsage::Iter, location)?; + } + } + // Interesting stuff about the __class__ variable: + // https://docs.python.org/3/reference/datamodel.html?highlight=__class__#creating-the-class-object + if context == ExpressionContext::Load + && self.tables.last().unwrap().typ == SymbolTableType::Function + && id == "super" + { + self.register_name("__class__", SymbolUsage::Used, location)?; + } + } + Lambda { args, body } => { + self.enter_function("lambda", args, expression.location.row())?; + match context { + ExpressionContext::IterDefinitionExp => { + self.scan_expression(body, ExpressionContext::IterDefinitionExp)?; + } + _ => { + self.scan_expression(body, ExpressionContext::Load)?; + } + } + self.leave_scope(); + } + IfExp { test, body, orelse } => { + self.scan_expression(test, ExpressionContext::Load)?; + self.scan_expression(body, ExpressionContext::Load)?; + self.scan_expression(orelse, ExpressionContext::Load)?; + } + + NamedExpr { target, value } => { + // named expressions are not allowed in the definition of + // comprehension iterator definitions + if let ExpressionContext::IterDefinitionExp = context { + return Err(SymbolTableError { + error: "assignment expression cannot be used in a comprehension iterable expression".to_string(), + // TODO: accurate location info, somehow + location: Location::default(), + }); + } + + self.scan_expression(value, ExpressionContext::Load)?; + + // special handling for assigned identifier in named expressions + // that are used in comprehensions. This required to correctly + // propagate the scope of the named assigned named and not to + // propagate inner names. + if let Name { id, .. } = &target.node { + let table = self.tables.last().unwrap(); + if table.typ == SymbolTableType::Comprehension { + self.register_name( + id, + SymbolUsage::AssignedNamedExprInComprehension, + location, + )?; + } 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, location)?; + } + } else { + self.scan_expression(target, ExpressionContext::Store)?; + } + } + } + Ok(()) + } + + fn scan_comprehension( + &mut self, + scope_name: &str, + elt1: &ast::Expr, + elt2: Option<&ast::Expr>, + generators: &[ast::Comprehension], + location: Location, + ) -> SymbolTableResult { + // Comprehensions are compiled as functions, so create a scope for them: + + self.enter_scope(scope_name, SymbolTableType::Comprehension, location.row()); + + // Register the passed argument to the generator function as the name ".0" + self.register_name(".0", SymbolUsage::Parameter, location)?; + + self.scan_expression(elt1, ExpressionContext::Load)?; + if let Some(elt2) = elt2 { + self.scan_expression(elt2, ExpressionContext::Load)?; + } + + let mut is_first_generator = true; + for generator in generators { + self.scan_expression(&generator.target, ExpressionContext::Iter)?; + if is_first_generator { + is_first_generator = false; + } else { + self.scan_expression(&generator.iter, ExpressionContext::IterDefinitionExp)?; + } + + for if_expr in &generator.ifs { + self.scan_expression(if_expr, ExpressionContext::Load)?; + } + } + + self.leave_scope(); + + // The first iterable is passed as an argument into the created function: + assert!(!generators.is_empty()); + self.scan_expression(&generators[0].iter, ExpressionContext::IterDefinitionExp)?; + + Ok(()) + } + + fn enter_function( + &mut self, + name: &str, + args: &ast::Arguments, + line_number: usize, + ) -> SymbolTableResult { + // Evaluate eventual default parameters: + self.scan_expressions(&args.defaults, ExpressionContext::Load)?; + for expression in args.kw_defaults.iter() { + self.scan_expression(expression, ExpressionContext::Load)?; + } + + // Annotations are scanned in outer scope: + self.scan_parameters_annotations(&args.posonlyargs)?; + self.scan_parameters_annotations(&args.args)?; + self.scan_parameters_annotations(&args.kwonlyargs)?; + if let Some(name) = &args.vararg { + self.scan_parameter_annotation(name)?; + } + if let Some(name) = &args.kwarg { + self.scan_parameter_annotation(name)?; + } + + 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_parameter(name)?; + } + if let Some(name) = &args.kwarg { + self.scan_parameter(name)?; + } + Ok(()) + } + + fn register_name( + &mut self, + name: &str, + role: SymbolUsage, + location: Location, + ) -> SymbolTableResult { + let scope_depth = self.tables.len(); + let table = self.tables.last_mut().unwrap(); + + let name = mangle_name(self.class_name.as_deref(), name); + + // Some checks for the symbol that present on this scope level: + let symbol = if let Some(symbol) = table.symbols.get_mut(name.as_ref()) { + let flags = &symbol.flags; + // Role already set.. + match role { + SymbolUsage::Global if !symbol.is_global() => { + if flags.contains(SymbolFlags::PARAMETER) { + return Err(SymbolTableError { + error: format!("name '{name}' is parameter and global"), + location, + }); + } + if flags.contains(SymbolFlags::REFERENCED) { + return Err(SymbolTableError { + error: format!("name '{name}' is used prior to global declaration"), + location, + }); + } + if flags.contains(SymbolFlags::ANNOTATED) { + return Err(SymbolTableError { + error: format!("annotated name '{name}' can't be global"), + location, + }); + } + if flags.contains(SymbolFlags::ASSIGNED) { + return Err(SymbolTableError { + error: format!( + "name '{name}' is assigned to before global declaration" + ), + location, + }); + } + } + SymbolUsage::Nonlocal => { + if flags.contains(SymbolFlags::PARAMETER) { + return Err(SymbolTableError { + error: format!("name '{name}' is parameter and nonlocal"), + location, + }); + } + if flags.contains(SymbolFlags::REFERENCED) { + return Err(SymbolTableError { + error: format!("name '{name}' is used prior to nonlocal declaration"), + location, + }); + } + if flags.contains(SymbolFlags::ANNOTATED) { + return Err(SymbolTableError { + error: format!("annotated name '{name}' can't be nonlocal"), + location, + }); + } + if flags.contains(SymbolFlags::ASSIGNED) { + return Err(SymbolTableError { + error: format!( + "name '{name}' is assigned to before nonlocal declaration" + ), + location, + }); + } + } + _ => { + // Ok? + } + } + symbol + } else { + // The symbol does not present on this scope level. + // Some checks to insert new symbol into symbol table: + match role { + SymbolUsage::Nonlocal if scope_depth < 2 => { + return Err(SymbolTableError { + error: format!("cannot define nonlocal '{name}' at top level."), + location, + }) + } + _ => { + // Ok! + } + } + // Insert symbol when required: + let symbol = Symbol::new(name.as_ref()); + table.symbols.entry(name.into_owned()).or_insert(symbol) + }; + + // Set proper scope and flags on symbol: + let flags = &mut symbol.flags; + match role { + SymbolUsage::Nonlocal => { + symbol.scope = SymbolScope::Free; + flags.insert(SymbolFlags::NONLOCAL); + } + SymbolUsage::Imported => { + flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::IMPORTED); + } + SymbolUsage::Parameter => { + flags.insert(SymbolFlags::PARAMETER); + } + SymbolUsage::AnnotationParameter => { + flags.insert(SymbolFlags::PARAMETER | SymbolFlags::ANNOTATED); + } + SymbolUsage::AnnotationAssigned => { + flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::ANNOTATED); + } + SymbolUsage::Assigned => { + flags.insert(SymbolFlags::ASSIGNED); + } + SymbolUsage::AssignedNamedExprInComprehension => { + flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::ASSIGNED_IN_COMPREHENSION); + } + SymbolUsage::Global => { + symbol.scope = SymbolScope::GlobalExplicit; + } + SymbolUsage::Used => { + flags.insert(SymbolFlags::REFERENCED); + } + SymbolUsage::Iter => { + flags.insert(SymbolFlags::ITER); + } + } + + // and even more checking + // it is not allowed to assign to iterator variables (by named expressions) + if flags.contains(SymbolFlags::ITER | SymbolFlags::ASSIGNED) + /*&& symbol.is_assign_named_expr_in_comprehension*/ + { + return Err(SymbolTableError { + error: + "assignment expression cannot be used in a comprehension iterable expression" + .to_string(), + location, + }); + } + Ok(()) + } +} + +pub(crate) fn mangle_name<'a>(class_name: Option<&str>, name: &'a str) -> Cow<'a, str> { + let class_name = match class_name { + Some(n) => n, + None => return name.into(), + }; + if !name.starts_with("__") || name.ends_with("__") || name.contains('.') { + return name.into(); + } + // strip leading underscore + let class_name = class_name.strip_prefix(|c| c == '_').unwrap_or(class_name); + let mut ret = String::with_capacity(1 + class_name.len() + name.len()); + ret.push('_'); + ret.push_str(class_name); + ret.push_str(name); + ret.into() +} diff --git a/core/Cargo.toml b/core/Cargo.toml index 5293ef43..79622a95 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,20 +1,18 @@ [package] -name = "rustpython-parser-core" -description = "RustPython parser data types." -edition = { workspace = true } -version = { workspace = true } -authors = { workspace = true } -repository = { workspace = true } -license = { workspace = true } -rust-version = { workspace = true } +name = "rustpython-compiler-core" +description = "RustPython specific bytecode." +version = "0.2.0" +authors = ["RustPython Team"] +edition = "2021" +repository = "https://github.com/RustPython/RustPython" +license = "MIT" [dependencies] -# vendored dependency shouldn't be placed out of this crate -rustpython-parser-vendored.workspace = true +bitflags = { workspace = true } +bstr = { workspace = true } +itertools = { workspace = true } +num-bigint = { workspace = true } +num-complex = { workspace = true } serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] } -is-macro.workspace = true -memchr.workspace = true -[features] -default = [] -location = [] +lz4_flex = "0.9.2" diff --git a/core/src/bytecode.rs b/core/src/bytecode.rs new file mode 100644 index 00000000..a522d3fb --- /dev/null +++ b/core/src/bytecode.rs @@ -0,0 +1,1610 @@ +//! Implement python as a virtual machine with bytecode. This module +//! implements bytecode structure. + +use crate::{marshal, Location}; +use bitflags::bitflags; +use itertools::Itertools; +use num_bigint::BigInt; +use num_complex::Complex64; +use std::marker::PhantomData; +use std::{collections::BTreeSet, fmt, hash, mem}; + +pub trait Constant: Sized { + type Name: AsRef; + + /// Transforms the given Constant to a BorrowedConstant + fn borrow_constant(&self) -> BorrowedConstant; +} + +impl Constant for ConstantData { + type Name = String; + fn borrow_constant(&self) -> BorrowedConstant { + use BorrowedConstant::*; + match self { + ConstantData::Integer { value } => Integer { value }, + ConstantData::Float { value } => Float { value: *value }, + ConstantData::Complex { value } => Complex { value: *value }, + ConstantData::Boolean { value } => Boolean { value: *value }, + ConstantData::Str { value } => Str { value }, + ConstantData::Bytes { value } => Bytes { value }, + ConstantData::Code { code } => Code { code }, + ConstantData::Tuple { elements } => Tuple { elements }, + ConstantData::None => None, + ConstantData::Ellipsis => Ellipsis, + } + } +} + +/// A Constant Bag +pub trait ConstantBag: Sized + Copy { + type Constant: Constant; + fn make_constant(&self, constant: BorrowedConstant) -> Self::Constant; + fn make_int(&self, value: BigInt) -> Self::Constant; + fn make_tuple(&self, elements: impl Iterator) -> Self::Constant; + fn make_code(&self, code: CodeObject) -> Self::Constant; + fn make_name(&self, name: &str) -> ::Name; +} + +pub trait AsBag { + type Bag: ConstantBag; + #[allow(clippy::wrong_self_convention)] + fn as_bag(self) -> Self::Bag; +} + +impl AsBag for Bag { + type Bag = Self; + fn as_bag(self) -> Self { + self + } +} + +#[derive(Clone, Copy)] +pub struct BasicBag; + +impl ConstantBag for BasicBag { + type Constant = ConstantData; + fn make_constant(&self, constant: BorrowedConstant) -> Self::Constant { + constant.to_owned() + } + fn make_int(&self, value: BigInt) -> Self::Constant { + ConstantData::Integer { value } + } + fn make_tuple(&self, elements: impl Iterator) -> Self::Constant { + ConstantData::Tuple { + elements: elements.collect(), + } + } + fn make_code(&self, code: CodeObject) -> Self::Constant { + ConstantData::Code { + code: Box::new(code), + } + } + fn make_name(&self, name: &str) -> ::Name { + name.to_owned() + } +} + +/// Primary container of a single code object. Each python function has +/// a code object. Also a module has a code object. +#[derive(Clone)] +pub struct CodeObject { + pub instructions: Box<[CodeUnit]>, + pub locations: Box<[Location]>, + pub flags: CodeFlags, + pub posonlyarg_count: u32, + // Number of positional-only arguments + pub arg_count: u32, + pub kwonlyarg_count: u32, + pub source_path: C::Name, + pub first_line_number: u32, + pub max_stackdepth: u32, + pub obj_name: C::Name, + // Name of the object that created this code object + pub cell2arg: Option>, + pub constants: Box<[C]>, + pub names: Box<[C::Name]>, + pub varnames: Box<[C::Name]>, + pub cellvars: Box<[C::Name]>, + pub freevars: Box<[C::Name]>, +} + +bitflags! { + pub struct CodeFlags: u16 { + const NEW_LOCALS = 0x01; + const IS_GENERATOR = 0x02; + const IS_COROUTINE = 0x04; + const HAS_VARARGS = 0x08; + const HAS_VARKEYWORDS = 0x10; + const IS_OPTIMIZED = 0x20; + } +} + +impl CodeFlags { + pub const NAME_MAPPING: &'static [(&'static str, CodeFlags)] = &[ + ("GENERATOR", CodeFlags::IS_GENERATOR), + ("COROUTINE", CodeFlags::IS_COROUTINE), + ( + "ASYNC_GENERATOR", + Self::from_bits_truncate(Self::IS_GENERATOR.bits | Self::IS_COROUTINE.bits), + ), + ("VARARGS", CodeFlags::HAS_VARARGS), + ("VARKEYWORDS", CodeFlags::HAS_VARKEYWORDS), + ]; +} + +/// an opcode argument that may be extended by a prior ExtendedArg +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(transparent)] +pub struct OpArgByte(pub u8); +impl OpArgByte { + pub const fn null() -> Self { + OpArgByte(0) + } +} +impl fmt::Debug for OpArgByte { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// a full 32-bit op_arg, including any possible ExtendedArg extension +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] +pub struct OpArg(pub u32); +impl OpArg { + pub const fn null() -> Self { + OpArg(0) + } + + /// Returns how many CodeUnits a instruction with this op_arg will be encoded as + #[inline] + pub fn instr_size(self) -> usize { + (self.0 > 0xff) as usize + (self.0 > 0xff_ff) as usize + (self.0 > 0xff_ff_ff) as usize + 1 + } + + /// returns the arg split into any necessary ExtendedArg components (in big-endian order) and + /// the arg for the real opcode itself + #[inline(always)] + pub fn split(self) -> (impl ExactSizeIterator, OpArgByte) { + let mut it = self + .0 + .to_le_bytes() + .map(OpArgByte) + .into_iter() + .take(self.instr_size()); + let lo = it.next().unwrap(); + (it.rev(), lo) + } +} + +#[derive(Default, Copy, Clone)] +#[repr(transparent)] +pub struct OpArgState { + state: u32, +} + +impl OpArgState { + #[inline(always)] + pub fn get(&mut self, ins: CodeUnit) -> (Instruction, OpArg) { + let arg = self.extend(ins.arg); + if ins.op != Instruction::ExtendedArg { + self.reset(); + } + (ins.op, arg) + } + #[inline(always)] + pub fn extend(&mut self, arg: OpArgByte) -> OpArg { + self.state = self.state << 8 | u32::from(arg.0); + OpArg(self.state) + } + #[inline(always)] + pub fn reset(&mut self) { + self.state = 0 + } +} + +pub trait OpArgType: Copy { + fn from_op_arg(x: u32) -> Option; + fn to_op_arg(self) -> u32; +} + +impl OpArgType for u32 { + #[inline(always)] + fn from_op_arg(x: u32) -> Option { + Some(x) + } + #[inline(always)] + fn to_op_arg(self) -> u32 { + self + } +} + +impl OpArgType for bool { + #[inline(always)] + fn from_op_arg(x: u32) -> Option { + Some(x != 0) + } + #[inline(always)] + fn to_op_arg(self) -> u32 { + self as u32 + } +} + +macro_rules! op_arg_enum { + ($(#[$attr:meta])* $vis:vis enum $name:ident { $($(#[$var_attr:meta])* $var:ident = $value:literal,)* }) => { + $(#[$attr])* + $vis enum $name { + $($(#[$var_attr])* $var = $value,)* + } + + impl OpArgType for $name { + fn to_op_arg(self) -> u32 { + self as u32 + } + fn from_op_arg(x: u32) -> Option { + Some(match u8::try_from(x).ok()? { + $($value => Self::$var,)* + _ => return None, + }) + } + } + }; +} + +#[derive(Copy, Clone)] +pub struct Arg(PhantomData); + +impl Arg { + #[inline] + pub fn marker() -> Self { + Arg(PhantomData) + } + #[inline] + pub fn new(arg: T) -> (Self, OpArg) { + (Self(PhantomData), OpArg(arg.to_op_arg())) + } + #[inline] + pub fn new_single(arg: T) -> (Self, OpArgByte) + where + T: Into, + { + (Self(PhantomData), OpArgByte(arg.into())) + } + #[inline(always)] + pub fn get(self, arg: OpArg) -> T { + self.try_get(arg).unwrap() + } + #[inline(always)] + pub fn try_get(self, arg: OpArg) -> Option { + T::from_op_arg(arg.0) + } + #[inline(always)] + /// # Safety + /// T::from_op_arg(self) must succeed + pub unsafe fn get_unchecked(self, arg: OpArg) -> T { + match T::from_op_arg(arg.0) { + Some(t) => t, + None => std::hint::unreachable_unchecked(), + } + } +} + +impl PartialEq for Arg { + fn eq(&self, _: &Self) -> bool { + true + } +} +impl Eq for Arg {} + +impl fmt::Debug for Arg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Arg<{}>", std::any::type_name::()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[repr(transparent)] +// XXX: if you add a new instruction that stores a Label, make sure to add it in +// Instruction::label_arg +pub struct Label(pub u32); + +impl OpArgType for Label { + #[inline(always)] + fn from_op_arg(x: u32) -> Option { + Some(Label(x)) + } + #[inline(always)] + fn to_op_arg(self) -> u32 { + self.0 + } +} + +impl fmt::Display for Label { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +op_arg_enum!( + /// Transforms a value prior to formatting it. + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + #[repr(u8)] + pub enum ConversionFlag { + /// No conversion + None = 0, // CPython uses -1 but not pleasure for us + /// Converts by calling `str()`. + Str = b's', + /// Converts by calling `ascii()`. + Ascii = b'a', + /// Converts by calling `repr()`. + Repr = b'r', + } +); + +impl TryFrom for ConversionFlag { + type Error = usize; + fn try_from(b: usize) -> Result { + u32::try_from(b).ok().and_then(Self::from_op_arg).ok_or(b) + } +} + +op_arg_enum!( + /// The kind of Raise that occurred. + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + #[repr(u8)] + pub enum RaiseKind { + Reraise = 0, + Raise = 1, + RaiseCause = 2, + } +); + +pub type NameIdx = u32; + +/// A Single bytecode instruction. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +pub enum Instruction { + /// Importing by name + ImportName { + idx: Arg, + }, + /// Importing without name + ImportNameless, + /// Import * + ImportStar, + /// from ... import ... + ImportFrom { + idx: Arg, + }, + LoadFast(Arg), + LoadNameAny(Arg), + LoadGlobal(Arg), + LoadDeref(Arg), + LoadClassDeref(Arg), + StoreFast(Arg), + StoreLocal(Arg), + StoreGlobal(Arg), + StoreDeref(Arg), + DeleteFast(Arg), + DeleteLocal(Arg), + DeleteGlobal(Arg), + DeleteDeref(Arg), + LoadClosure(Arg), + Subscript, + StoreSubscript, + DeleteSubscript, + StoreAttr { + idx: Arg, + }, + DeleteAttr { + idx: Arg, + }, + LoadConst { + /// index into constants vec + idx: Arg, + }, + UnaryOperation { + op: Arg, + }, + BinaryOperation { + op: Arg, + }, + BinaryOperationInplace { + op: Arg, + }, + LoadAttr { + idx: Arg, + }, + TestOperation { + op: Arg, + }, + CompareOperation { + op: Arg, + }, + Pop, + Rotate2, + Rotate3, + Duplicate, + Duplicate2, + GetIter, + Continue { + target: Arg AddAssign for TextRange -where - TextRange: Add, -{ - #[inline] - fn add_assign(&mut self, rhs: A) { - *self = *self + rhs; - } -} - -impl SubAssign for TextRange -where - TextRange: Sub, -{ - #[inline] - fn sub_assign(&mut self, rhs: S) { - *self = *self - rhs; - } -} diff --git a/vendored/src/text_size/schemars_impls.rs b/vendored/src/text_size/schemars_impls.rs deleted file mode 100644 index a1c7fa36..00000000 --- a/vendored/src/text_size/schemars_impls.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! This module implements the [`JsonSchema`] trait from the `schemars` crate for -//! [`TextSize`] and [`TextRange`] if the `schemars` feature is enabled. This trait -//! exposes meta-information on how a given type is serialized and deserialized -//! using `serde`, and is currently used to generate autocomplete information -//! for the `rome.json` configuration file and TypeScript types for the node.js -//! bindings to the Workspace API - -use crate::{TextRange, TextSize}; -use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema}; - -impl JsonSchema for TextSize { - fn schema_name() -> String { - String::from("TextSize") - } - - fn json_schema(gen: &mut SchemaGenerator) -> Schema { - // TextSize is represented as a raw u32, see serde_impls.rs for the - // actual implementation - ::json_schema(gen) - } -} - -impl JsonSchema for TextRange { - fn schema_name() -> String { - String::from("TextRange") - } - - fn json_schema(gen: &mut SchemaGenerator) -> Schema { - // TextSize is represented as (TextSize, TextSize), see serde_impls.rs - // for the actual implementation - <(TextSize, TextSize)>::json_schema(gen) - } -} diff --git a/vendored/src/text_size/serde_impls.rs b/vendored/src/text_size/serde_impls.rs deleted file mode 100644 index 9dace574..00000000 --- a/vendored/src/text_size/serde_impls.rs +++ /dev/null @@ -1,47 +0,0 @@ -use { - super::{TextRange, TextSize}, - serde::{de, Deserialize, Deserializer, Serialize, Serializer}, -}; - -impl Serialize for TextSize { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.raw.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for TextSize { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - u32::deserialize(deserializer).map(TextSize::from) - } -} - -impl Serialize for TextRange { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - (self.start(), self.end()).serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for TextRange { - #[allow(clippy::nonminimal_bool)] - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let (start, end) = Deserialize::deserialize(deserializer)?; - if !(start <= end) { - return Err(de::Error::custom(format!( - "invalid range: {start:?}..{end:?}" - ))); - } - Ok(TextRange::new(start, end)) - } -} diff --git a/vendored/src/text_size/size.rs b/vendored/src/text_size/size.rs deleted file mode 100644 index 147eab26..00000000 --- a/vendored/src/text_size/size.rs +++ /dev/null @@ -1,196 +0,0 @@ -use { - super::TextLen, - std::{ - convert::TryFrom, - fmt, iter, - num::TryFromIntError, - ops::{Add, AddAssign, Sub, SubAssign}, - }, -}; - -/// A measure of text length. Also, equivalently, an index into text. -/// -/// This is a UTF-8 bytes offset stored as `u32`, but -/// most clients should treat it as an opaque measure. -/// -/// For cases that need to escape `TextSize` and return to working directly -/// with primitive integers, `TextSize` can be converted losslessly to/from -/// `u32` via [`From`] conversions as well as losslessly be converted [`Into`] -/// `usize`. The `usize -> TextSize` direction can be done via [`TryFrom`]. -/// -/// These escape hatches are primarily required for unit testing and when -/// converting from UTF-8 size to another coordinate space, such as UTF-16. -#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TextSize { - pub(crate) raw: u32, -} - -impl fmt::Debug for TextSize { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.raw) - } -} - -impl TextSize { - /// Creates a new `TextSize` at the given `offset`. - /// - /// # Examples - /// - /// ```rust - /// # use rustpython_parser_vendored::text_size::*; - /// assert_eq!(TextSize::from(4), TextSize::new(4)); - /// ``` - pub const fn new(offset: u32) -> Self { - Self { raw: offset } - } - - /// The text size of some primitive text-like object. - /// - /// Accepts `char`, `&str`, and `&String`. - /// - /// # Examples - /// - /// ```rust - /// # use rustpython_parser_vendored::text_size::*; - /// let char_size = TextSize::of('🦀'); - /// assert_eq!(char_size, TextSize::from(4)); - /// - /// let str_size = TextSize::of("rust-analyzer"); - /// assert_eq!(str_size, TextSize::from(13)); - /// ``` - #[inline] - pub fn of(text: T) -> TextSize { - text.text_len() - } - - /// Returns current raw `offset` as u32. - /// - /// # Examples - /// - /// ```rust - /// # use rustpython_parser_vendored::text_size::*; - /// assert_eq!(TextSize::from(4).to_u32(), 4); - /// ``` - pub fn to_u32(&self) -> u32 { - self.raw - } - - /// Returns current raw `offset` as usize. - /// - /// # Examples - /// - /// ```rust - /// # use rustpython_parser_vendored::text_size::*; - /// assert_eq!(TextSize::from(4).to_usize(), 4); - /// ``` - pub fn to_usize(&self) -> usize { - self.raw as usize - } -} - -/// Methods to act like a primitive integer type, where reasonably applicable. -// Last updated for parity with Rust 1.42.0. -impl TextSize { - /// Checked addition. Returns `None` if overflow occurred. - #[inline] - pub fn checked_add(self, rhs: TextSize) -> Option { - self.raw.checked_add(rhs.raw).map(|raw| TextSize { raw }) - } - - /// Checked subtraction. Returns `None` if overflow occurred. - #[inline] - pub fn checked_sub(self, rhs: TextSize) -> Option { - self.raw.checked_sub(rhs.raw).map(|raw| TextSize { raw }) - } -} - -impl From for TextSize { - #[inline] - fn from(raw: u32) -> Self { - TextSize::new(raw) - } -} - -impl From for u32 { - #[inline] - fn from(value: TextSize) -> Self { - value.to_u32() - } -} - -impl TryFrom for TextSize { - type Error = TryFromIntError; - #[inline] - fn try_from(value: usize) -> Result { - Ok(u32::try_from(value)?.into()) - } -} - -impl From for usize { - #[inline] - fn from(value: TextSize) -> Self { - value.to_usize() - } -} - -macro_rules! ops { - (impl $Op:ident for TextSize by fn $f:ident = $op:tt) => { - impl $Op for TextSize { - type Output = TextSize; - #[inline] - fn $f(self, other: TextSize) -> TextSize { - TextSize { raw: self.raw $op other.raw } - } - } - impl $Op<&TextSize> for TextSize { - type Output = TextSize; - #[inline] - fn $f(self, other: &TextSize) -> TextSize { - self $op *other - } - } - impl $Op for &TextSize - where - TextSize: $Op, - { - type Output = TextSize; - #[inline] - fn $f(self, other: T) -> TextSize { - *self $op other - } - } - }; -} - -ops!(impl Add for TextSize by fn add = +); -ops!(impl Sub for TextSize by fn sub = -); - -impl AddAssign for TextSize -where - TextSize: Add, -{ - #[inline] - fn add_assign(&mut self, rhs: A) { - *self = *self + rhs; - } -} - -impl SubAssign for TextSize -where - TextSize: Sub, -{ - #[inline] - fn sub_assign(&mut self, rhs: S) { - *self = *self - rhs; - } -} - -impl iter::Sum for TextSize -where - TextSize: Add, -{ - #[inline] - fn sum>(iter: I) -> TextSize { - iter.fold(0.into(), Add::add) - } -} diff --git a/vendored/src/text_size/traits.rs b/vendored/src/text_size/traits.rs deleted file mode 100644 index b72d2de5..00000000 --- a/vendored/src/text_size/traits.rs +++ /dev/null @@ -1,37 +0,0 @@ -use {super::TextSize, std::convert::TryInto}; - -use priv_in_pub::Sealed; -mod priv_in_pub { - pub trait Sealed {} -} - -/// Primitives with a textual length that can be passed to [`TextSize::of`]. -pub trait TextLen: Copy + Sealed { - /// The textual length of this primitive. - fn text_len(self) -> TextSize; -} - -impl Sealed for &'_ str {} -impl TextLen for &'_ str { - #[inline] - fn text_len(self) -> TextSize { - self.len().try_into().unwrap() - } -} - -impl Sealed for &'_ String {} -impl TextLen for &'_ String { - #[inline] - fn text_len(self) -> TextSize { - self.as_str().text_len() - } -} - -impl Sealed for char {} -impl TextLen for char { - #[inline] - #[allow(clippy::cast_possible_truncation)] - fn text_len(self) -> TextSize { - (self.len_utf8() as u32).into() - } -}