Files
RustPython/crates/vm/src/stdlib/_ast/operator.rs
T
Jeong, YunWon a1ba6a662c Align compiler and AST behavior with CPython (#8138)
* Align compiler and AST behavior with CPython

Update the compiler pipeline, AST module support, and related VM compile/eval paths toward CPython 3.14 behavior.

Use upstream Ruff parser crates directly and keep RustPython-specific syntax preflight handling in the compile path.

Refresh opcode metadata, snapshots, and targeted tests for the aligned bytecode and AST behavior.

* fix

* Fix PR 8138 CI failures

* Fix match mapping wildcard scan complexity

* fixes

* Use Ruff runtime AST fields

* Minimize residual patch diff

* refactor error handling

* Remove _ast conversion context wrappers

* Clean up _ast conversion glue and compiler warning escalation

Rename the Node conversion parameter `ctx` to `vm` throughout the _ast
module.

Remove the unused compile_program, compile_program_single,
compile_block_expression, and compile_expression forwarders; compile_top
stays as the entry point.

Stash an escalated compiler SyntaxWarning so a non-SyntaxWarning category
propagates unchanged instead of being rewritten to SyntaxError, matching
PyErr_ExceptionMatches(SyntaxWarning) in compiler_warn. Drop the now-unused
CompileWarningError::into_codegen_error.

Replace the PositionalArguments two-variant enum with a struct holding a
shared range and a PositionalArgumentsKind enum.

Assisted-by: Claude

* Represent compiler flags as a CompilerFlags bitflags

Replace the loose PY_CF_*/CO_* i32 constants in compile_mode with a
CompilerFlags bitflags covering the PyCF_* flags and __future__
CO_FUTURE_* bits. The Python-visible ast.PyCF_* attribute values are
derived from it via bits(); parser input modes stay plain consts.

builtins::compile and the compile_string flag handling test flags
through CompilerFlags::contains instead of raw bit math.

Assisted-by: Claude

* Fix PR 8138 test regressions

* Fix PR 8138 CI failures

* Avoid shell interpolation in Linux deps action

* Fix review findings in compiler/AST changes

- frame.rs: compute the MATCH_CLASS class name lazily so a successful
  match no longer takes the type name lock or allocates; the
  __match_args__-not-a-tuple branch reuses it instead of recomputing.
- _ast/constant.rs: add the missing leading space to the with_recursion
  label ("maximum recursion depth exceeded during compilation").
- sys.set_int_max_str_digits: restore the "maxdigits must be 0 or larger
  than {n}" error text.
- codegen: set CO_MAXBLOCKS to 20 in both ir.rs and compile.rs.
- _ast/exception.rs: extract the duplicated excepthandler node guard
  into ensure_excepthandler_node.
- codegen: correct the validate_keywords doc comment.
- Drop redundant "CPython" qualifiers from comments added in this branch.

Assisted-by: Claude
2026-06-26 18:22:08 +09:00

186 lines
7.7 KiB
Rust

use super::*;
use rustpython_compiler_core::SourceFile;
// sum
impl Node for ast::BoolOp {
fn ast_to_object(self, vm: &VirtualMachine, _source_file: &SourceFile) -> PyObjectRef {
let node_type = match self {
Self::And => pyast::NodeBoolOpAnd::static_type(),
Self::Or => pyast::NodeBoolOpOr::static_type(),
};
singleton_node_to_object(vm, node_type)
}
fn ast_from_object(
vm: &VirtualMachine,
_source_file: &SourceFile,
object: PyObjectRef,
) -> PyResult<Self> {
Ok(
if is_node_instance(vm, &object, pyast::NodeBoolOpAnd::static_type())? {
Self::And
} else if is_node_instance(vm, &object, pyast::NodeBoolOpOr::static_type())? {
Self::Or
} else {
return Err(vm.new_type_error(format!(
"expected some sort of boolop, but got {}",
object.repr(vm)?
)));
},
)
}
}
// sum
impl Node for ast::Operator {
fn ast_to_object(self, vm: &VirtualMachine, _source_file: &SourceFile) -> PyObjectRef {
let node_type = match self {
Self::Add => pyast::NodeOperatorAdd::static_type(),
Self::Sub => pyast::NodeOperatorSub::static_type(),
Self::Mult => pyast::NodeOperatorMult::static_type(),
Self::MatMult => pyast::NodeOperatorMatMult::static_type(),
Self::Div => pyast::NodeOperatorDiv::static_type(),
Self::Mod => pyast::NodeOperatorMod::static_type(),
Self::Pow => pyast::NodeOperatorPow::static_type(),
Self::LShift => pyast::NodeOperatorLShift::static_type(),
Self::RShift => pyast::NodeOperatorRShift::static_type(),
Self::BitOr => pyast::NodeOperatorBitOr::static_type(),
Self::BitXor => pyast::NodeOperatorBitXor::static_type(),
Self::BitAnd => pyast::NodeOperatorBitAnd::static_type(),
Self::FloorDiv => pyast::NodeOperatorFloorDiv::static_type(),
};
singleton_node_to_object(vm, node_type)
}
fn ast_from_object(
vm: &VirtualMachine,
_source_file: &SourceFile,
object: PyObjectRef,
) -> PyResult<Self> {
Ok(
if is_node_instance(vm, &object, pyast::NodeOperatorAdd::static_type())? {
Self::Add
} else if is_node_instance(vm, &object, pyast::NodeOperatorSub::static_type())? {
Self::Sub
} else if is_node_instance(vm, &object, pyast::NodeOperatorMult::static_type())? {
Self::Mult
} else if is_node_instance(vm, &object, pyast::NodeOperatorMatMult::static_type())? {
Self::MatMult
} else if is_node_instance(vm, &object, pyast::NodeOperatorDiv::static_type())? {
Self::Div
} else if is_node_instance(vm, &object, pyast::NodeOperatorMod::static_type())? {
Self::Mod
} else if is_node_instance(vm, &object, pyast::NodeOperatorPow::static_type())? {
Self::Pow
} else if is_node_instance(vm, &object, pyast::NodeOperatorLShift::static_type())? {
Self::LShift
} else if is_node_instance(vm, &object, pyast::NodeOperatorRShift::static_type())? {
Self::RShift
} else if is_node_instance(vm, &object, pyast::NodeOperatorBitOr::static_type())? {
Self::BitOr
} else if is_node_instance(vm, &object, pyast::NodeOperatorBitXor::static_type())? {
Self::BitXor
} else if is_node_instance(vm, &object, pyast::NodeOperatorBitAnd::static_type())? {
Self::BitAnd
} else if is_node_instance(vm, &object, pyast::NodeOperatorFloorDiv::static_type())? {
Self::FloorDiv
} else {
return Err(vm.new_type_error(format!(
"expected some sort of operator, but got {}",
object.repr(vm)?
)));
},
)
}
}
// sum
impl Node for ast::UnaryOp {
fn ast_to_object(self, vm: &VirtualMachine, _source_file: &SourceFile) -> PyObjectRef {
let node_type = match self {
Self::Invert => pyast::NodeUnaryOpInvert::static_type(),
Self::Not => pyast::NodeUnaryOpNot::static_type(),
Self::UAdd => pyast::NodeUnaryOpUAdd::static_type(),
Self::USub => pyast::NodeUnaryOpUSub::static_type(),
};
singleton_node_to_object(vm, node_type)
}
fn ast_from_object(
vm: &VirtualMachine,
_source_file: &SourceFile,
object: PyObjectRef,
) -> PyResult<Self> {
Ok(
if is_node_instance(vm, &object, pyast::NodeUnaryOpInvert::static_type())? {
Self::Invert
} else if is_node_instance(vm, &object, pyast::NodeUnaryOpNot::static_type())? {
Self::Not
} else if is_node_instance(vm, &object, pyast::NodeUnaryOpUAdd::static_type())? {
Self::UAdd
} else if is_node_instance(vm, &object, pyast::NodeUnaryOpUSub::static_type())? {
Self::USub
} else {
return Err(vm.new_type_error(format!(
"expected some sort of unaryop, but got {}",
object.repr(vm)?
)));
},
)
}
}
// sum
impl Node for ast::CmpOp {
fn ast_to_object(self, vm: &VirtualMachine, _source_file: &SourceFile) -> PyObjectRef {
let node_type = match self {
Self::Eq => pyast::NodeCmpOpEq::static_type(),
Self::NotEq => pyast::NodeCmpOpNotEq::static_type(),
Self::Lt => pyast::NodeCmpOpLt::static_type(),
Self::LtE => pyast::NodeCmpOpLtE::static_type(),
Self::Gt => pyast::NodeCmpOpGt::static_type(),
Self::GtE => pyast::NodeCmpOpGtE::static_type(),
Self::Is => pyast::NodeCmpOpIs::static_type(),
Self::IsNot => pyast::NodeCmpOpIsNot::static_type(),
Self::In => pyast::NodeCmpOpIn::static_type(),
Self::NotIn => pyast::NodeCmpOpNotIn::static_type(),
};
singleton_node_to_object(vm, node_type)
}
fn ast_from_object(
vm: &VirtualMachine,
_source_file: &SourceFile,
object: PyObjectRef,
) -> PyResult<Self> {
Ok(
if is_node_instance(vm, &object, pyast::NodeCmpOpEq::static_type())? {
Self::Eq
} else if is_node_instance(vm, &object, pyast::NodeCmpOpNotEq::static_type())? {
Self::NotEq
} else if is_node_instance(vm, &object, pyast::NodeCmpOpLt::static_type())? {
Self::Lt
} else if is_node_instance(vm, &object, pyast::NodeCmpOpLtE::static_type())? {
Self::LtE
} else if is_node_instance(vm, &object, pyast::NodeCmpOpGt::static_type())? {
Self::Gt
} else if is_node_instance(vm, &object, pyast::NodeCmpOpGtE::static_type())? {
Self::GtE
} else if is_node_instance(vm, &object, pyast::NodeCmpOpIs::static_type())? {
Self::Is
} else if is_node_instance(vm, &object, pyast::NodeCmpOpIsNot::static_type())? {
Self::IsNot
} else if is_node_instance(vm, &object, pyast::NodeCmpOpIn::static_type())? {
Self::In
} else if is_node_instance(vm, &object, pyast::NodeCmpOpNotIn::static_type())? {
Self::NotIn
} else {
return Err(vm.new_type_error(format!(
"expected some sort of cmpop, but got {}",
object.repr(vm)?
)));
},
)
}
}