mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
398 lines
13 KiB
Rust
398 lines
13 KiB
Rust
use super::*;
|
|
use crate::builtins::{PyComplex, PyFrozenSet, PyTuple};
|
|
use ruff::str_prefix::StringLiteralPrefix;
|
|
|
|
#[derive(Debug)]
|
|
pub(super) struct Constant {
|
|
pub(super) range: TextRange,
|
|
pub(super) value: ConstantLiteral,
|
|
}
|
|
|
|
impl Constant {
|
|
pub(super) fn new_str(
|
|
value: impl Into<Box<str>>,
|
|
prefix: StringLiteralPrefix,
|
|
range: TextRange,
|
|
) -> Self {
|
|
let value = value.into();
|
|
Self {
|
|
range,
|
|
value: ConstantLiteral::Str { value, prefix },
|
|
}
|
|
}
|
|
|
|
pub(super) const fn new_int(value: ruff::Int, range: TextRange) -> Self {
|
|
Self {
|
|
range,
|
|
value: ConstantLiteral::Int(value),
|
|
}
|
|
}
|
|
|
|
pub(super) const fn new_float(value: f64, range: TextRange) -> Self {
|
|
Self {
|
|
range,
|
|
value: ConstantLiteral::Float(value),
|
|
}
|
|
}
|
|
|
|
pub(super) const fn new_complex(real: f64, imag: f64, range: TextRange) -> Self {
|
|
Self {
|
|
range,
|
|
value: ConstantLiteral::Complex { real, imag },
|
|
}
|
|
}
|
|
|
|
pub(super) const fn new_bytes(value: Box<[u8]>, range: TextRange) -> Self {
|
|
Self {
|
|
range,
|
|
value: ConstantLiteral::Bytes(value),
|
|
}
|
|
}
|
|
|
|
pub(super) const fn new_bool(value: bool, range: TextRange) -> Self {
|
|
Self {
|
|
range,
|
|
value: ConstantLiteral::Bool(value),
|
|
}
|
|
}
|
|
|
|
pub(super) const fn new_none(range: TextRange) -> Self {
|
|
Self {
|
|
range,
|
|
value: ConstantLiteral::None,
|
|
}
|
|
}
|
|
|
|
pub(super) const fn new_ellipsis(range: TextRange) -> Self {
|
|
Self {
|
|
range,
|
|
value: ConstantLiteral::Ellipsis,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn into_expr(self) -> ruff::Expr {
|
|
constant_to_ruff_expr(self)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) enum ConstantLiteral {
|
|
None,
|
|
Bool(bool),
|
|
Str {
|
|
value: Box<str>,
|
|
prefix: StringLiteralPrefix,
|
|
},
|
|
Bytes(Box<[u8]>),
|
|
Int(ruff::Int),
|
|
Tuple(Vec<ConstantLiteral>),
|
|
FrozenSet(Vec<ConstantLiteral>),
|
|
Float(f64),
|
|
Complex {
|
|
real: f64,
|
|
imag: f64,
|
|
},
|
|
Ellipsis,
|
|
}
|
|
|
|
// constructor
|
|
impl Node for Constant {
|
|
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
|
let Self { range, value } = self;
|
|
let node = NodeAst
|
|
.into_ref_with_type(vm, pyast::NodeExprConstant::static_type().to_owned())
|
|
.unwrap();
|
|
let kind = match &value {
|
|
ConstantLiteral::Str {
|
|
prefix: StringLiteralPrefix::Unicode,
|
|
..
|
|
} => vm.ctx.new_str("u").into(),
|
|
_ => vm.ctx.none(),
|
|
};
|
|
let value = value.ast_to_object(vm, source_code);
|
|
let dict = node.as_object().dict().unwrap();
|
|
dict.set_item("value", value, vm).unwrap();
|
|
dict.set_item("kind", kind, vm).unwrap();
|
|
node_add_location(&dict, range, vm, source_code);
|
|
node.into()
|
|
}
|
|
|
|
fn ast_from_object(
|
|
vm: &VirtualMachine,
|
|
source_code: &SourceCodeOwned,
|
|
object: PyObjectRef,
|
|
) -> PyResult<Self> {
|
|
let value_object = get_node_field(vm, &object, "value", "Constant")?;
|
|
let value = Node::ast_from_object(vm, source_code, value_object)?;
|
|
|
|
Ok(Self {
|
|
value,
|
|
// kind: get_node_field_opt(_vm, &_object, "kind")?
|
|
// .map(|obj| Node::ast_from_object(_vm, obj))
|
|
// .transpose()?,
|
|
range: range_from_object(vm, source_code, object, "Constant")?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Node for ConstantLiteral {
|
|
fn ast_to_object(self, vm: &VirtualMachine, source_code: &SourceCodeOwned) -> PyObjectRef {
|
|
match self {
|
|
Self::None => vm.ctx.none(),
|
|
Self::Bool(value) => vm.ctx.new_bool(value).to_pyobject(vm),
|
|
Self::Str { value, .. } => vm.ctx.new_str(value).to_pyobject(vm),
|
|
Self::Bytes(value) => vm.ctx.new_bytes(value.into()).to_pyobject(vm),
|
|
Self::Int(value) => value.ast_to_object(vm, source_code),
|
|
Self::Tuple(value) => {
|
|
let value = value
|
|
.into_iter()
|
|
.map(|c| c.ast_to_object(vm, source_code))
|
|
.collect();
|
|
vm.ctx.new_tuple(value).to_pyobject(vm)
|
|
}
|
|
Self::FrozenSet(value) => PyFrozenSet::from_iter(
|
|
vm,
|
|
value.into_iter().map(|c| c.ast_to_object(vm, source_code)),
|
|
)
|
|
.unwrap()
|
|
.into_pyobject(vm),
|
|
Self::Float(value) => vm.ctx.new_float(value).into_pyobject(vm),
|
|
Self::Complex { real, imag } => vm
|
|
.ctx
|
|
.new_complex(num_complex::Complex::new(real, imag))
|
|
.into_pyobject(vm),
|
|
Self::Ellipsis => vm.ctx.ellipsis.clone().into(),
|
|
}
|
|
}
|
|
|
|
fn ast_from_object(
|
|
vm: &VirtualMachine,
|
|
source_code: &SourceCodeOwned,
|
|
value_object: PyObjectRef,
|
|
) -> PyResult<Self> {
|
|
let cls = value_object.class();
|
|
let value = if cls.is(vm.ctx.types.none_type) {
|
|
Self::None
|
|
} else if cls.is(vm.ctx.types.bool_type) {
|
|
Self::Bool(if value_object.is(&vm.ctx.true_value) {
|
|
true
|
|
} else if value_object.is(&vm.ctx.false_value) {
|
|
false
|
|
} else {
|
|
value_object.try_to_value(vm)?
|
|
})
|
|
} else if cls.is(vm.ctx.types.str_type) {
|
|
Self::Str {
|
|
value: value_object.try_to_value::<String>(vm)?.into(),
|
|
prefix: StringLiteralPrefix::Empty,
|
|
}
|
|
} else if cls.is(vm.ctx.types.bytes_type) {
|
|
Self::Bytes(value_object.try_to_value::<Vec<u8>>(vm)?.into())
|
|
} else if cls.is(vm.ctx.types.int_type) {
|
|
Self::Int(Node::ast_from_object(vm, source_code, value_object)?)
|
|
} else if cls.is(vm.ctx.types.tuple_type) {
|
|
let tuple = value_object.downcast::<PyTuple>().map_err(|obj| {
|
|
vm.new_type_error(format!(
|
|
"Expected type {}, not {}",
|
|
PyTuple::static_type().name(),
|
|
obj.class().name()
|
|
))
|
|
})?;
|
|
let tuple = tuple
|
|
.into_iter()
|
|
.cloned()
|
|
.map(|object| Node::ast_from_object(vm, source_code, object))
|
|
.collect::<PyResult<_>>()?;
|
|
Self::Tuple(tuple)
|
|
} else if cls.is(vm.ctx.types.frozenset_type) {
|
|
let set = value_object.downcast::<PyFrozenSet>().unwrap();
|
|
let elements = set
|
|
.elements()
|
|
.into_iter()
|
|
.map(|object| Node::ast_from_object(vm, source_code, object))
|
|
.collect::<PyResult<_>>()?;
|
|
Self::FrozenSet(elements)
|
|
} else if cls.is(vm.ctx.types.float_type) {
|
|
let float = value_object.try_into_value(vm)?;
|
|
Self::Float(float)
|
|
} else if cls.is(vm.ctx.types.complex_type) {
|
|
let complex = value_object.try_complex(vm)?;
|
|
let complex = match complex {
|
|
None => {
|
|
return Err(vm.new_type_error(format!(
|
|
"Expected type {}, not {}",
|
|
PyComplex::static_type().name(),
|
|
value_object.class().name()
|
|
)));
|
|
}
|
|
Some((value, _was_coerced)) => value,
|
|
};
|
|
Self::Complex {
|
|
real: complex.re,
|
|
imag: complex.im,
|
|
}
|
|
} else if cls.is(vm.ctx.types.ellipsis_type) {
|
|
Self::Ellipsis
|
|
} else {
|
|
return Err(vm.new_type_error(format!(
|
|
"got an invalid type in Constant: {}",
|
|
value_object.class().name()
|
|
)));
|
|
};
|
|
Ok(value)
|
|
}
|
|
}
|
|
|
|
fn constant_to_ruff_expr(value: Constant) -> ruff::Expr {
|
|
let Constant { value, range } = value;
|
|
match value {
|
|
ConstantLiteral::None => ruff::Expr::NoneLiteral(ruff::ExprNoneLiteral { range }),
|
|
ConstantLiteral::Bool(value) => {
|
|
ruff::Expr::BooleanLiteral(ruff::ExprBooleanLiteral { range, value })
|
|
}
|
|
ConstantLiteral::Str { value, prefix } => {
|
|
ruff::Expr::StringLiteral(ruff::ExprStringLiteral {
|
|
range,
|
|
value: ruff::StringLiteralValue::single(ruff::StringLiteral {
|
|
range,
|
|
value,
|
|
flags: ruff::StringLiteralFlags::empty().with_prefix(prefix),
|
|
}),
|
|
})
|
|
}
|
|
ConstantLiteral::Bytes(value) => {
|
|
ruff::Expr::BytesLiteral(ruff::ExprBytesLiteral {
|
|
range,
|
|
value: ruff::BytesLiteralValue::single(ruff::BytesLiteral {
|
|
range,
|
|
value,
|
|
flags: ruff::BytesLiteralFlags::empty(), // TODO
|
|
}),
|
|
})
|
|
}
|
|
ConstantLiteral::Int(value) => ruff::Expr::NumberLiteral(ruff::ExprNumberLiteral {
|
|
range,
|
|
value: ruff::Number::Int(value),
|
|
}),
|
|
ConstantLiteral::Tuple(value) => ruff::Expr::Tuple(ruff::ExprTuple {
|
|
range,
|
|
elts: value
|
|
.into_iter()
|
|
.map(|value| {
|
|
constant_to_ruff_expr(Constant {
|
|
range: TextRange::default(),
|
|
value,
|
|
})
|
|
})
|
|
.collect(),
|
|
ctx: ruff::ExprContext::Load,
|
|
// TODO: Does this matter?
|
|
parenthesized: true,
|
|
}),
|
|
ConstantLiteral::FrozenSet(value) => ruff::Expr::Call(ruff::ExprCall {
|
|
range,
|
|
// idk lol
|
|
func: Box::new(ruff::Expr::Name(ruff::ExprName {
|
|
range: TextRange::default(),
|
|
id: ruff::name::Name::new_static("frozenset"),
|
|
ctx: ruff::ExprContext::Load,
|
|
})),
|
|
arguments: ruff::Arguments {
|
|
range,
|
|
args: value
|
|
.into_iter()
|
|
.map(|value| {
|
|
constant_to_ruff_expr(Constant {
|
|
range: TextRange::default(),
|
|
value,
|
|
})
|
|
})
|
|
.collect(),
|
|
keywords: Box::default(),
|
|
},
|
|
}),
|
|
ConstantLiteral::Float(value) => ruff::Expr::NumberLiteral(ruff::ExprNumberLiteral {
|
|
range,
|
|
value: ruff::Number::Float(value),
|
|
}),
|
|
ConstantLiteral::Complex { real, imag } => {
|
|
ruff::Expr::NumberLiteral(ruff::ExprNumberLiteral {
|
|
range,
|
|
value: ruff::Number::Complex { real, imag },
|
|
})
|
|
}
|
|
ConstantLiteral::Ellipsis => {
|
|
ruff::Expr::EllipsisLiteral(ruff::ExprEllipsisLiteral { range })
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(super) fn number_literal_to_object(
|
|
vm: &VirtualMachine,
|
|
source_code: &SourceCodeOwned,
|
|
constant: ruff::ExprNumberLiteral,
|
|
) -> PyObjectRef {
|
|
let ruff::ExprNumberLiteral { range, value } = constant;
|
|
let c = match value {
|
|
ruff::Number::Int(n) => Constant::new_int(n, range),
|
|
ruff::Number::Float(n) => Constant::new_float(n, range),
|
|
ruff::Number::Complex { real, imag } => Constant::new_complex(real, imag, range),
|
|
};
|
|
c.ast_to_object(vm, source_code)
|
|
}
|
|
|
|
pub(super) fn string_literal_to_object(
|
|
vm: &VirtualMachine,
|
|
source_code: &SourceCodeOwned,
|
|
constant: ruff::ExprStringLiteral,
|
|
) -> PyObjectRef {
|
|
let ruff::ExprStringLiteral { range, value } = constant;
|
|
let prefix = value
|
|
.iter()
|
|
.next()
|
|
.map_or(StringLiteralPrefix::Empty, |part| part.flags.prefix());
|
|
let c = Constant::new_str(value.to_str(), prefix, range);
|
|
c.ast_to_object(vm, source_code)
|
|
}
|
|
|
|
pub(super) fn bytes_literal_to_object(
|
|
vm: &VirtualMachine,
|
|
source_code: &SourceCodeOwned,
|
|
constant: ruff::ExprBytesLiteral,
|
|
) -> PyObjectRef {
|
|
let ruff::ExprBytesLiteral { range, value } = constant;
|
|
let bytes = value.as_slice().iter().flat_map(|b| b.value.iter());
|
|
let c = Constant::new_bytes(bytes.copied().collect(), range);
|
|
c.ast_to_object(vm, source_code)
|
|
}
|
|
|
|
pub(super) fn boolean_literal_to_object(
|
|
vm: &VirtualMachine,
|
|
source_code: &SourceCodeOwned,
|
|
constant: ruff::ExprBooleanLiteral,
|
|
) -> PyObjectRef {
|
|
let ruff::ExprBooleanLiteral { range, value } = constant;
|
|
let c = Constant::new_bool(value, range);
|
|
c.ast_to_object(vm, source_code)
|
|
}
|
|
|
|
pub(super) fn none_literal_to_object(
|
|
vm: &VirtualMachine,
|
|
source_code: &SourceCodeOwned,
|
|
constant: ruff::ExprNoneLiteral,
|
|
) -> PyObjectRef {
|
|
let ruff::ExprNoneLiteral { range } = constant;
|
|
let c = Constant::new_none(range);
|
|
c.ast_to_object(vm, source_code)
|
|
}
|
|
|
|
pub(super) fn ellipsis_literal_to_object(
|
|
vm: &VirtualMachine,
|
|
source_code: &SourceCodeOwned,
|
|
constant: ruff::ExprEllipsisLiteral,
|
|
) -> PyObjectRef {
|
|
let ruff::ExprEllipsisLiteral { range } = constant;
|
|
let c = Constant::new_ellipsis(range);
|
|
c.ast_to_object(vm, source_code)
|
|
}
|