mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Merge pull request #5633 from coolreader18/compiler-deps
This commit is contained in:
28
Cargo.lock
generated
28
Cargo.lock
generated
@@ -2185,29 +2185,6 @@ dependencies = [
|
||||
"rustc-hash 2.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_codegen"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a"
|
||||
dependencies = [
|
||||
"ruff_python_ast",
|
||||
"ruff_python_literal",
|
||||
"ruff_python_parser",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_literal"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"itertools 0.14.0",
|
||||
"ruff_python_ast",
|
||||
"unic-ucd-category",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_parser"
|
||||
version = "0.0.0"
|
||||
@@ -2323,12 +2300,11 @@ dependencies = [
|
||||
"num-complex",
|
||||
"num-traits",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_codegen",
|
||||
"ruff_python_parser",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustpython-compiler-core",
|
||||
"rustpython-compiler-source",
|
||||
"rustpython-literal",
|
||||
"rustpython-wtf8",
|
||||
"thiserror 2.0.11",
|
||||
"unicode_names2",
|
||||
@@ -2386,8 +2362,6 @@ dependencies = [
|
||||
"lz4_flex",
|
||||
"malachite-bigint",
|
||||
"num-complex",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_parser",
|
||||
"ruff_source_file",
|
||||
"rustpython-wtf8",
|
||||
"serde",
|
||||
|
||||
@@ -148,7 +148,6 @@ ruff_python_parser = { git = "https://github.com/astral-sh/ruff.git", tag = "0.1
|
||||
ruff_python_ast = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
|
||||
ruff_source_file = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
|
||||
ruff_python_codegen = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
|
||||
|
||||
ahash = "0.8.11"
|
||||
ascii = "1.1"
|
||||
|
||||
6
Lib/test/test_future_stmt/test_future.py
vendored
6
Lib/test/test_future_stmt/test_future.py
vendored
@@ -198,8 +198,6 @@ class AnnotationsFutureTestCase(unittest.TestCase):
|
||||
)
|
||||
return scope
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_annotations(self):
|
||||
eq = self.assertAnnotationEqual
|
||||
eq('...')
|
||||
@@ -364,8 +362,6 @@ class AnnotationsFutureTestCase(unittest.TestCase):
|
||||
eq('(((a, b)))', '(a, b)')
|
||||
eq("1 + 2 + 3")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_fstring_debug_annotations(self):
|
||||
# f-strings with '=' don't round trip very well, so set the expected
|
||||
# result explicitly.
|
||||
@@ -376,8 +372,6 @@ class AnnotationsFutureTestCase(unittest.TestCase):
|
||||
self.assertAnnotationEqual("f'{x=!a}'", expected="f'x={x!a}'")
|
||||
self.assertAnnotationEqual("f'{x=!s:*^20}'", expected="f'x={x!s:*^20}'")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_infinity_numbers(self):
|
||||
inf = "1e" + repr(sys.float_info.max_10_exp + 1)
|
||||
infj = f"{inf}j"
|
||||
|
||||
@@ -14,12 +14,11 @@ license.workspace = true
|
||||
# rustpython-parser-core = { workspace = true }
|
||||
rustpython-compiler-core = { workspace = true }
|
||||
rustpython-compiler-source = {workspace = true }
|
||||
rustpython-literal = {workspace = true }
|
||||
rustpython-wtf8 = { workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
ruff_source_file = { workspace = true }
|
||||
ruff_python_codegen = { workspace = true }
|
||||
|
||||
ahash = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::{
|
||||
error::{CodegenError, CodegenErrorType},
|
||||
ir,
|
||||
symboltable::{self, SymbolFlags, SymbolScope, SymbolTable},
|
||||
unparse::unparse_expr,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use malachite_bigint::BigInt;
|
||||
@@ -2026,11 +2027,10 @@ impl Compiler<'_> {
|
||||
|
||||
fn compile_annotation(&mut self, annotation: &Expr) -> CompileResult<()> {
|
||||
if self.future_annotations {
|
||||
// FIXME: codegen?
|
||||
let ident = Default::default();
|
||||
let codegen = ruff_python_codegen::Generator::new(&ident, Default::default());
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: codegen.expr(annotation).into(),
|
||||
value: unparse_expr(annotation, &self.source_code)
|
||||
.to_string()
|
||||
.into(),
|
||||
});
|
||||
} else {
|
||||
self.compile_expression(annotation)?;
|
||||
@@ -3397,7 +3397,9 @@ impl Compiler<'_> {
|
||||
flags: FStringFlags,
|
||||
fstring_elements: &FStringElements,
|
||||
) -> CompileResult<()> {
|
||||
let mut element_count = 0;
|
||||
for element in fstring_elements {
|
||||
element_count += 1;
|
||||
match element {
|
||||
FStringElement::Literal(string) => {
|
||||
if string.value.contains(char::REPLACEMENT_CHARACTER) {
|
||||
@@ -3419,26 +3421,14 @@ impl Compiler<'_> {
|
||||
FStringElement::Expression(fstring_expr) => {
|
||||
let mut conversion = fstring_expr.conversion;
|
||||
|
||||
let debug_text_count = match &fstring_expr.debug_text {
|
||||
None => 0,
|
||||
Some(DebugText { leading, trailing }) => {
|
||||
let range = fstring_expr.expression.range();
|
||||
let source = self.source_code.get_range(range);
|
||||
let source = source.to_string();
|
||||
if let Some(DebugText { leading, trailing }) = &fstring_expr.debug_text {
|
||||
let range = fstring_expr.expression.range();
|
||||
let source = self.source_code.get_range(range);
|
||||
let text = [leading, source, trailing].concat();
|
||||
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: leading.to_string().into(),
|
||||
});
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: source.into(),
|
||||
});
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: trailing.to_string().into(),
|
||||
});
|
||||
|
||||
3
|
||||
}
|
||||
};
|
||||
self.emit_load_const(ConstantData::Str { value: text.into() });
|
||||
element_count += 1;
|
||||
}
|
||||
|
||||
match &fstring_expr.format_spec {
|
||||
None => {
|
||||
@@ -3447,7 +3437,9 @@ impl Compiler<'_> {
|
||||
});
|
||||
// Match CPython behavior: If debug text is present, apply repr conversion.
|
||||
// See: https://github.com/python/cpython/blob/f61afca262d3a0aa6a8a501db0b1936c60858e35/Parser/action_helpers.c#L1456
|
||||
if conversion == ConversionFlag::None && debug_text_count > 0 {
|
||||
if conversion == ConversionFlag::None
|
||||
&& fstring_expr.debug_text.is_some()
|
||||
{
|
||||
conversion = ConversionFlag::Repr;
|
||||
}
|
||||
}
|
||||
@@ -3458,30 +3450,17 @@ impl Compiler<'_> {
|
||||
|
||||
self.compile_expression(&fstring_expr.expression)?;
|
||||
|
||||
emit!(
|
||||
self,
|
||||
Instruction::FormatValue {
|
||||
conversion: conversion
|
||||
}
|
||||
);
|
||||
|
||||
// concatenate formatted string and debug text (if present)
|
||||
if debug_text_count > 0 {
|
||||
emit!(
|
||||
self,
|
||||
Instruction::BuildString {
|
||||
size: debug_text_count + 1
|
||||
}
|
||||
);
|
||||
}
|
||||
let conversion = match conversion {
|
||||
ConversionFlag::None => bytecode::ConversionFlag::None,
|
||||
ConversionFlag::Str => bytecode::ConversionFlag::Str,
|
||||
ConversionFlag::Ascii => bytecode::ConversionFlag::Ascii,
|
||||
ConversionFlag::Repr => bytecode::ConversionFlag::Repr,
|
||||
};
|
||||
emit!(self, Instruction::FormatValue { conversion });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let element_count: u32 = fstring_elements
|
||||
.len()
|
||||
.try_into()
|
||||
.expect("BuildString size overflowed");
|
||||
if element_count == 0 {
|
||||
// ensure to put an empty string on the stack if there aren't any fstring elements
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
|
||||
@@ -13,6 +13,7 @@ pub mod error;
|
||||
pub mod ir;
|
||||
mod string_parser;
|
||||
pub mod symboltable;
|
||||
mod unparse;
|
||||
|
||||
pub use compile::CompileOpts;
|
||||
use ruff_python_ast::Expr;
|
||||
|
||||
624
compiler/codegen/src/unparse.rs
Normal file
624
compiler/codegen/src/unparse.rs
Normal file
@@ -0,0 +1,624 @@
|
||||
use ruff_python_ast as ruff;
|
||||
use ruff_text_size::Ranged;
|
||||
use rustpython_compiler_source::SourceCode;
|
||||
use rustpython_literal::escape::{AsciiEscape, UnicodeEscape};
|
||||
use std::fmt::{self, Display as _};
|
||||
|
||||
use ruff::{
|
||||
Arguments, BoolOp, Comprehension, ConversionFlag, Expr, Identifier, Operator, Parameter,
|
||||
ParameterWithDefault, Parameters,
|
||||
};
|
||||
|
||||
mod precedence {
|
||||
macro_rules! precedence {
|
||||
($($op:ident,)*) => {
|
||||
precedence!(@0, $($op,)*);
|
||||
};
|
||||
(@$i:expr, $op1:ident, $($op:ident,)*) => {
|
||||
pub const $op1: u8 = $i;
|
||||
precedence!(@$i + 1, $($op,)*);
|
||||
};
|
||||
(@$i:expr,) => {};
|
||||
}
|
||||
precedence!(
|
||||
TUPLE, TEST, OR, AND, NOT, CMP, // "EXPR" =
|
||||
BOR, BXOR, BAND, SHIFT, ARITH, TERM, FACTOR, POWER, AWAIT, ATOM,
|
||||
);
|
||||
pub const EXPR: u8 = BOR;
|
||||
}
|
||||
|
||||
struct Unparser<'a, 'b, 'c> {
|
||||
f: &'b mut fmt::Formatter<'a>,
|
||||
source: &'c SourceCode<'c>,
|
||||
}
|
||||
impl<'a, 'b, 'c> Unparser<'a, 'b, 'c> {
|
||||
fn new(f: &'b mut fmt::Formatter<'a>, source: &'c SourceCode<'c>) -> Self {
|
||||
Unparser { f, source }
|
||||
}
|
||||
|
||||
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)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn p_delim(&mut self, first: &mut bool, s: &str) -> fmt::Result {
|
||||
self.p_if(!std::mem::take(first), s)
|
||||
}
|
||||
fn write_fmt(&mut self, f: fmt::Arguments<'_>) -> fmt::Result {
|
||||
self.f.write_fmt(f)
|
||||
}
|
||||
|
||||
fn unparse_expr(&mut self, ast: &Expr, level: u8) -> fmt::Result {
|
||||
macro_rules! op_prec {
|
||||
($op_ty:ident, $x:expr, $enu:path, $($var:ident($op:literal, $prec:ident)),*$(,)?) => {
|
||||
match $x {
|
||||
$(<$enu>::$var => (op_prec!(@space $op_ty, $op), precedence::$prec),)*
|
||||
}
|
||||
};
|
||||
(@space bin, $op:literal) => {
|
||||
concat!(" ", $op, " ")
|
||||
};
|
||||
(@space un, $op:literal) => {
|
||||
$op
|
||||
};
|
||||
}
|
||||
macro_rules! group_if {
|
||||
($lvl:expr, $body:block) => {{
|
||||
let group = level > $lvl;
|
||||
self.p_if(group, "(")?;
|
||||
let ret = $body;
|
||||
self.p_if(group, ")")?;
|
||||
ret
|
||||
}};
|
||||
}
|
||||
match &ast {
|
||||
Expr::BoolOp(ruff::ExprBoolOp {
|
||||
op,
|
||||
values,
|
||||
range: _range,
|
||||
}) => {
|
||||
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 {
|
||||
self.p_delim(&mut first, op)?;
|
||||
self.unparse_expr(val, prec + 1)?;
|
||||
}
|
||||
})
|
||||
}
|
||||
Expr::Named(ruff::ExprNamed {
|
||||
target,
|
||||
value,
|
||||
range: _range,
|
||||
}) => {
|
||||
group_if!(precedence::TUPLE, {
|
||||
self.unparse_expr(target, precedence::ATOM)?;
|
||||
self.p(" := ")?;
|
||||
self.unparse_expr(value, precedence::ATOM)?;
|
||||
})
|
||||
}
|
||||
Expr::BinOp(ruff::ExprBinOp {
|
||||
left,
|
||||
op,
|
||||
right,
|
||||
range: _range,
|
||||
}) => {
|
||||
let right_associative = matches!(op, Operator::Pow);
|
||||
let (op, prec) = op_prec!(
|
||||
bin,
|
||||
op,
|
||||
Operator,
|
||||
Add("+", ARITH),
|
||||
Sub("-", ARITH),
|
||||
Mult("*", TERM),
|
||||
MatMult("@", TERM),
|
||||
Div("/", TERM),
|
||||
Mod("%", TERM),
|
||||
Pow("**", POWER),
|
||||
LShift("<<", SHIFT),
|
||||
RShift(">>", SHIFT),
|
||||
BitOr("|", BOR),
|
||||
BitXor("^", BXOR),
|
||||
BitAnd("&", BAND),
|
||||
FloorDiv("//", TERM),
|
||||
);
|
||||
group_if!(prec, {
|
||||
self.unparse_expr(left, prec + right_associative as u8)?;
|
||||
self.p(op)?;
|
||||
self.unparse_expr(right, prec + !right_associative as u8)?;
|
||||
})
|
||||
}
|
||||
Expr::UnaryOp(ruff::ExprUnaryOp {
|
||||
op,
|
||||
operand,
|
||||
range: _range,
|
||||
}) => {
|
||||
let (op, prec) = op_prec!(
|
||||
un,
|
||||
op,
|
||||
ruff::UnaryOp,
|
||||
Invert("~", FACTOR),
|
||||
Not("not ", NOT),
|
||||
UAdd("+", FACTOR),
|
||||
USub("-", FACTOR)
|
||||
);
|
||||
group_if!(prec, {
|
||||
self.p(op)?;
|
||||
self.unparse_expr(operand, prec)?;
|
||||
})
|
||||
}
|
||||
Expr::Lambda(ruff::ExprLambda {
|
||||
parameters,
|
||||
body,
|
||||
range: _range,
|
||||
}) => {
|
||||
group_if!(precedence::TEST, {
|
||||
if let Some(parameters) = parameters {
|
||||
self.p("lambda ")?;
|
||||
self.unparse_arguments(parameters)?;
|
||||
} else {
|
||||
self.p("lambda")?;
|
||||
}
|
||||
write!(self, ": {}", unparse_expr(body, self.source))?;
|
||||
})
|
||||
}
|
||||
Expr::If(ruff::ExprIf {
|
||||
test,
|
||||
body,
|
||||
orelse,
|
||||
range: _range,
|
||||
}) => {
|
||||
group_if!(precedence::TEST, {
|
||||
self.unparse_expr(body, precedence::TEST + 1)?;
|
||||
self.p(" if ")?;
|
||||
self.unparse_expr(test, precedence::TEST + 1)?;
|
||||
self.p(" else ")?;
|
||||
self.unparse_expr(orelse, precedence::TEST)?;
|
||||
})
|
||||
}
|
||||
Expr::Dict(ruff::ExprDict {
|
||||
items,
|
||||
range: _range,
|
||||
}) => {
|
||||
self.p("{")?;
|
||||
let mut first = true;
|
||||
for item in items {
|
||||
self.p_delim(&mut first, ", ")?;
|
||||
if let Some(k) = &item.key {
|
||||
write!(self, "{}: ", unparse_expr(k, self.source))?;
|
||||
} else {
|
||||
self.p("**")?;
|
||||
}
|
||||
self.unparse_expr(&item.value, level)?;
|
||||
}
|
||||
self.p("}")?;
|
||||
}
|
||||
Expr::Set(ruff::ExprSet {
|
||||
elts,
|
||||
range: _range,
|
||||
}) => {
|
||||
self.p("{")?;
|
||||
let mut first = true;
|
||||
for v in elts {
|
||||
self.p_delim(&mut first, ", ")?;
|
||||
self.unparse_expr(v, precedence::TEST)?;
|
||||
}
|
||||
self.p("}")?;
|
||||
}
|
||||
Expr::ListComp(ruff::ExprListComp {
|
||||
elt,
|
||||
generators,
|
||||
range: _range,
|
||||
}) => {
|
||||
self.p("[")?;
|
||||
self.unparse_expr(elt, precedence::TEST)?;
|
||||
self.unparse_comp(generators)?;
|
||||
self.p("]")?;
|
||||
}
|
||||
Expr::SetComp(ruff::ExprSetComp {
|
||||
elt,
|
||||
generators,
|
||||
range: _range,
|
||||
}) => {
|
||||
self.p("{")?;
|
||||
self.unparse_expr(elt, precedence::TEST)?;
|
||||
self.unparse_comp(generators)?;
|
||||
self.p("}")?;
|
||||
}
|
||||
Expr::DictComp(ruff::ExprDictComp {
|
||||
key,
|
||||
value,
|
||||
generators,
|
||||
range: _range,
|
||||
}) => {
|
||||
self.p("{")?;
|
||||
self.unparse_expr(key, precedence::TEST)?;
|
||||
self.p(": ")?;
|
||||
self.unparse_expr(value, precedence::TEST)?;
|
||||
self.unparse_comp(generators)?;
|
||||
self.p("}")?;
|
||||
}
|
||||
Expr::Generator(ruff::ExprGenerator {
|
||||
parenthesized: _,
|
||||
elt,
|
||||
generators,
|
||||
range: _range,
|
||||
}) => {
|
||||
self.p("(")?;
|
||||
self.unparse_expr(elt, precedence::TEST)?;
|
||||
self.unparse_comp(generators)?;
|
||||
self.p(")")?;
|
||||
}
|
||||
Expr::Await(ruff::ExprAwait {
|
||||
value,
|
||||
range: _range,
|
||||
}) => {
|
||||
group_if!(precedence::AWAIT, {
|
||||
self.p("await ")?;
|
||||
self.unparse_expr(value, precedence::ATOM)?;
|
||||
})
|
||||
}
|
||||
Expr::Yield(ruff::ExprYield {
|
||||
value,
|
||||
range: _range,
|
||||
}) => {
|
||||
if let Some(value) = value {
|
||||
write!(self, "(yield {})", unparse_expr(value, self.source))?;
|
||||
} else {
|
||||
self.p("(yield)")?;
|
||||
}
|
||||
}
|
||||
Expr::YieldFrom(ruff::ExprYieldFrom {
|
||||
value,
|
||||
range: _range,
|
||||
}) => {
|
||||
write!(self, "(yield from {})", unparse_expr(value, self.source))?;
|
||||
}
|
||||
Expr::Compare(ruff::ExprCompare {
|
||||
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(" ")?;
|
||||
self.unparse_expr(cmp, new_lvl)?;
|
||||
}
|
||||
})
|
||||
}
|
||||
Expr::Call(ruff::ExprCall {
|
||||
func,
|
||||
arguments: Arguments { args, keywords, .. },
|
||||
range: _range,
|
||||
}) => {
|
||||
self.unparse_expr(func, precedence::ATOM)?;
|
||||
self.p("(")?;
|
||||
if let (
|
||||
[
|
||||
Expr::Generator(ruff::ExprGenerator {
|
||||
elt,
|
||||
generators,
|
||||
range: _range,
|
||||
..
|
||||
}),
|
||||
],
|
||||
[],
|
||||
) = (&**args, &**keywords)
|
||||
{
|
||||
// make sure a single genexpr doesn't get double parens
|
||||
self.unparse_expr(elt, precedence::TEST)?;
|
||||
self.unparse_comp(generators)?;
|
||||
} else {
|
||||
let mut first = true;
|
||||
for arg in args {
|
||||
self.p_delim(&mut first, ", ")?;
|
||||
self.unparse_expr(arg, precedence::TEST)?;
|
||||
}
|
||||
for kw in keywords {
|
||||
self.p_delim(&mut first, ", ")?;
|
||||
if let Some(arg) = &kw.arg {
|
||||
self.p_id(arg)?;
|
||||
self.p("=")?;
|
||||
} else {
|
||||
self.p("**")?;
|
||||
}
|
||||
self.unparse_expr(&kw.value, precedence::TEST)?;
|
||||
}
|
||||
}
|
||||
self.p(")")?;
|
||||
}
|
||||
Expr::FString(ruff::ExprFString { value, .. }) => self.unparse_fstring(value)?,
|
||||
Expr::StringLiteral(ruff::ExprStringLiteral { value, .. }) => {
|
||||
if value.is_unicode() {
|
||||
self.p("u")?
|
||||
}
|
||||
UnicodeEscape::new_repr(value.to_str().as_ref())
|
||||
.str_repr()
|
||||
.fmt(self.f)?
|
||||
}
|
||||
Expr::BytesLiteral(ruff::ExprBytesLiteral { value, .. }) => {
|
||||
AsciiEscape::new_repr(&value.bytes().collect::<Vec<_>>())
|
||||
.bytes_repr()
|
||||
.fmt(self.f)?
|
||||
}
|
||||
Expr::NumberLiteral(ruff::ExprNumberLiteral { value, .. }) => {
|
||||
const { assert!(f64::MAX_10_EXP == 308) };
|
||||
let inf_str = "1e309";
|
||||
match value {
|
||||
ruff::Number::Int(int) => int.fmt(self.f)?,
|
||||
&ruff::Number::Float(fp) => {
|
||||
if fp.is_infinite() {
|
||||
self.p(inf_str)?
|
||||
} else {
|
||||
self.p(&rustpython_literal::float::to_string(fp))?
|
||||
}
|
||||
}
|
||||
&ruff::Number::Complex { real, imag } => self
|
||||
.p(&rustpython_literal::float::complex_to_string(real, imag)
|
||||
.replace("inf", inf_str))?,
|
||||
}
|
||||
}
|
||||
Expr::BooleanLiteral(ruff::ExprBooleanLiteral { value, .. }) => {
|
||||
self.p(if *value { "True" } else { "False" })?
|
||||
}
|
||||
Expr::NoneLiteral(ruff::ExprNoneLiteral { .. }) => self.p("None")?,
|
||||
Expr::EllipsisLiteral(ruff::ExprEllipsisLiteral { .. }) => self.p("...")?,
|
||||
Expr::Attribute(ruff::ExprAttribute { value, attr, .. }) => {
|
||||
self.unparse_expr(value, precedence::ATOM)?;
|
||||
let period = if let Expr::NumberLiteral(ruff::ExprNumberLiteral {
|
||||
value: ruff::Number::Int(_),
|
||||
..
|
||||
}) = value.as_ref()
|
||||
{
|
||||
" ."
|
||||
} else {
|
||||
"."
|
||||
};
|
||||
self.p(period)?;
|
||||
self.p_id(attr)?;
|
||||
}
|
||||
Expr::Subscript(ruff::ExprSubscript { value, slice, .. }) => {
|
||||
self.unparse_expr(value, precedence::ATOM)?;
|
||||
let lvl = precedence::TUPLE;
|
||||
self.p("[")?;
|
||||
self.unparse_expr(slice, lvl)?;
|
||||
self.p("]")?;
|
||||
}
|
||||
Expr::Starred(ruff::ExprStarred { value, .. }) => {
|
||||
self.p("*")?;
|
||||
self.unparse_expr(value, precedence::EXPR)?;
|
||||
}
|
||||
Expr::Name(ruff::ExprName { id, .. }) => self.p(id.as_str())?,
|
||||
Expr::List(ruff::ExprList { elts, .. }) => {
|
||||
self.p("[")?;
|
||||
let mut first = true;
|
||||
for elt in elts {
|
||||
self.p_delim(&mut first, ", ")?;
|
||||
self.unparse_expr(elt, precedence::TEST)?;
|
||||
}
|
||||
self.p("]")?;
|
||||
}
|
||||
Expr::Tuple(ruff::ExprTuple { elts, .. }) => {
|
||||
if elts.is_empty() {
|
||||
self.p("()")?;
|
||||
} else {
|
||||
group_if!(precedence::TUPLE, {
|
||||
let mut first = true;
|
||||
for elt in elts {
|
||||
self.p_delim(&mut first, ", ")?;
|
||||
self.unparse_expr(elt, precedence::TEST)?;
|
||||
}
|
||||
self.p_if(elts.len() == 1, ",")?;
|
||||
})
|
||||
}
|
||||
}
|
||||
Expr::Slice(ruff::ExprSlice {
|
||||
lower,
|
||||
upper,
|
||||
step,
|
||||
range: _range,
|
||||
}) => {
|
||||
if let Some(lower) = lower {
|
||||
self.unparse_expr(lower, precedence::TEST)?;
|
||||
}
|
||||
self.p(":")?;
|
||||
if let Some(upper) = upper {
|
||||
self.unparse_expr(upper, precedence::TEST)?;
|
||||
}
|
||||
if let Some(step) = step {
|
||||
self.p(":")?;
|
||||
self.unparse_expr(step, precedence::TEST)?;
|
||||
}
|
||||
}
|
||||
Expr::IpyEscapeCommand(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unparse_arguments(&mut self, args: &Parameters) -> 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: &ParameterWithDefault) -> fmt::Result {
|
||||
self.unparse_arg(&arg.parameter)?;
|
||||
if let Some(default) = &arg.default {
|
||||
write!(self, "={}", unparse_expr(default, self.source))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unparse_arg(&mut self, arg: &Parameter) -> fmt::Result {
|
||||
self.p_id(&arg.name)?;
|
||||
if let Some(ann) = &arg.annotation {
|
||||
write!(self, ": {}", unparse_expr(ann, self.source))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unparse_comp(&mut self, generators: &[Comprehension]) -> fmt::Result {
|
||||
for comp in generators {
|
||||
self.p(if comp.is_async {
|
||||
" async for "
|
||||
} else {
|
||||
" for "
|
||||
})?;
|
||||
self.unparse_expr(&comp.target, precedence::TUPLE)?;
|
||||
self.p(" in ")?;
|
||||
self.unparse_expr(&comp.iter, precedence::TEST + 1)?;
|
||||
for cond in &comp.ifs {
|
||||
self.p(" if ")?;
|
||||
self.unparse_expr(cond, precedence::TEST + 1)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unparse_fstring_body(&mut self, elements: &[ruff::FStringElement]) -> fmt::Result {
|
||||
for elem in elements {
|
||||
self.unparse_fstring_elem(elem)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unparse_formatted(
|
||||
&mut self,
|
||||
val: &Expr,
|
||||
debug_text: Option<&ruff::DebugText>,
|
||||
conversion: ConversionFlag,
|
||||
spec: Option<&ruff::FStringFormatSpec>,
|
||||
) -> fmt::Result {
|
||||
let buffered = to_string_fmt(|f| {
|
||||
Unparser::new(f, self.source).unparse_expr(val, precedence::TEST + 1)
|
||||
});
|
||||
if let Some(ruff::DebugText { leading, trailing }) = debug_text {
|
||||
self.p(leading)?;
|
||||
self.p(self.source.get_range(val.range()))?;
|
||||
self.p(trailing)?;
|
||||
}
|
||||
let brace = if buffered.starts_with('{') {
|
||||
// put a space to avoid escaping the bracket
|
||||
"{ "
|
||||
} else {
|
||||
"{"
|
||||
};
|
||||
self.p(brace)?;
|
||||
self.p(&buffered)?;
|
||||
drop(buffered);
|
||||
|
||||
if conversion != ConversionFlag::None {
|
||||
self.p("!")?;
|
||||
let buf = &[conversion as u8];
|
||||
let c = std::str::from_utf8(buf).unwrap();
|
||||
self.p(c)?;
|
||||
}
|
||||
|
||||
if let Some(spec) = spec {
|
||||
self.p(":")?;
|
||||
self.unparse_fstring_body(&spec.elements)?;
|
||||
}
|
||||
|
||||
self.p("}")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unparse_fstring_elem(&mut self, elem: &ruff::FStringElement) -> fmt::Result {
|
||||
match elem {
|
||||
ruff::FStringElement::Expression(ruff::FStringExpressionElement {
|
||||
expression,
|
||||
debug_text,
|
||||
conversion,
|
||||
format_spec,
|
||||
..
|
||||
}) => self.unparse_formatted(
|
||||
expression,
|
||||
debug_text.as_ref(),
|
||||
*conversion,
|
||||
format_spec.as_deref(),
|
||||
),
|
||||
ruff::FStringElement::Literal(ruff::FStringLiteralElement { value, .. }) => {
|
||||
self.unparse_fstring_str(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unparse_fstring_str(&mut self, s: &str) -> fmt::Result {
|
||||
let s = s.replace('{', "{{").replace('}', "}}");
|
||||
self.p(&s)
|
||||
}
|
||||
|
||||
fn unparse_fstring(&mut self, value: &ruff::FStringValue) -> fmt::Result {
|
||||
self.p("f")?;
|
||||
let body = to_string_fmt(|f| {
|
||||
value.iter().try_for_each(|part| match part {
|
||||
ruff::FStringPart::Literal(lit) => f.write_str(lit),
|
||||
ruff::FStringPart::FString(ruff::FString { elements, .. }) => {
|
||||
Unparser::new(f, self.source).unparse_fstring_body(elements)
|
||||
}
|
||||
})
|
||||
});
|
||||
// .unparse_fstring_body(elements));
|
||||
UnicodeEscape::new_repr(body.as_str().as_ref())
|
||||
.str_repr()
|
||||
.write(self.f)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnparseExpr<'a> {
|
||||
expr: &'a Expr,
|
||||
source: &'a SourceCode<'a>,
|
||||
}
|
||||
|
||||
pub fn unparse_expr<'a>(expr: &'a Expr, source: &'a SourceCode<'a>) -> UnparseExpr<'a> {
|
||||
UnparseExpr { expr, source }
|
||||
}
|
||||
|
||||
impl fmt::Display for UnparseExpr<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Unparser::new(f, self.source).unparse_expr(self.expr, precedence::TEST)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_string_fmt(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> String {
|
||||
use std::cell::Cell;
|
||||
struct Fmt<F>(Cell<Option<F>>);
|
||||
impl<F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Display for Fmt<F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.take().unwrap()(f)
|
||||
}
|
||||
}
|
||||
Fmt(Cell::new(Some(f))).to_string()
|
||||
}
|
||||
@@ -10,8 +10,6 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
# rustpython-parser-core = { workspace = true, features=["location"] }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ruff_python_parser = { workspace = true }
|
||||
ruff_source_file = { workspace = true }
|
||||
rustpython-wtf8 = { workspace = true }
|
||||
|
||||
|
||||
@@ -5,14 +5,24 @@ use bitflags::bitflags;
|
||||
use itertools::Itertools;
|
||||
use malachite_bigint::BigInt;
|
||||
use num_complex::Complex64;
|
||||
pub use ruff_python_ast::ConversionFlag;
|
||||
// use rustpython_parser_core::source_code::{OneIndexed, SourceLocation};
|
||||
use ruff_source_file::{OneIndexed, SourceLocation};
|
||||
use rustpython_wtf8::{Wtf8, Wtf8Buf};
|
||||
use std::marker::PhantomData;
|
||||
use std::{collections::BTreeSet, fmt, hash, mem};
|
||||
|
||||
// pub use rustpython_parser_core::ConversionFlag;
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
#[repr(i8)]
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
pub enum ConversionFlag {
|
||||
/// No conversion
|
||||
None = -1, // CPython uses -1
|
||||
/// Converts by calling `str(<value>)`.
|
||||
Str = b's' as i8,
|
||||
/// Converts by calling `ascii(<value>)`.
|
||||
Ascii = b'a' as i8,
|
||||
/// Converts by calling `repr(<value>)`.
|
||||
Repr = b'r' as i8,
|
||||
}
|
||||
|
||||
pub trait Constant: Sized {
|
||||
type Name: AsRef<str>;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
pub use ruff_python_parser::ModeParseError;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Mode {
|
||||
Exec,
|
||||
Eval,
|
||||
Single,
|
||||
/// Returns the value of the last statement in the statement list.
|
||||
BlockExpr,
|
||||
}
|
||||
|
||||
@@ -22,14 +21,12 @@ impl std::str::FromStr for Mode {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mode> for ruff_python_parser::Mode {
|
||||
fn from(mode: Mode) -> Self {
|
||||
match mode {
|
||||
Mode::Exec => Self::Module,
|
||||
Mode::Eval => Self::Expression,
|
||||
// TODO: Improve ruff API
|
||||
// ruff does not have an interactive mode
|
||||
Mode::Single | Mode::BlockExpr => Self::Ipython,
|
||||
}
|
||||
/// Returned when a given mode is not valid.
|
||||
#[derive(Debug)]
|
||||
pub struct ModeParseError;
|
||||
|
||||
impl std::fmt::Display for ModeParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, r#"mode must be "exec", "eval", or "single""#)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,6 +223,39 @@ pub fn to_string(value: f64) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn complex_to_string(re: f64, im: f64) -> String {
|
||||
// integer => drop ., fractional => float_ops
|
||||
let mut im_part = if im.fract() == 0.0 {
|
||||
im.to_string()
|
||||
} else {
|
||||
to_string(im)
|
||||
};
|
||||
im_part.push('j');
|
||||
|
||||
// positive empty => return im_part, integer => drop ., fractional => float_ops
|
||||
let re_part = if re == 0.0 {
|
||||
if re.is_sign_positive() {
|
||||
return im_part;
|
||||
} else {
|
||||
re.to_string()
|
||||
}
|
||||
} else if re.fract() == 0.0 {
|
||||
re.to_string()
|
||||
} else {
|
||||
to_string(re)
|
||||
};
|
||||
let mut result =
|
||||
String::with_capacity(re_part.len() + im_part.len() + 2 + im.is_sign_positive() as usize);
|
||||
result.push('(');
|
||||
result.push_str(&re_part);
|
||||
if im.is_sign_positive() || im.is_nan() {
|
||||
result.push('+');
|
||||
}
|
||||
result.push_str(&im_part);
|
||||
result.push(')');
|
||||
result
|
||||
}
|
||||
|
||||
pub fn from_hex(s: &str) -> Option<f64> {
|
||||
if let Ok(f) = hexf_parse::parse_hexf64(s, false) {
|
||||
return Some(f);
|
||||
|
||||
@@ -119,7 +119,14 @@ fn _compile(
|
||||
mode: Mode,
|
||||
opts: CompileOpts,
|
||||
) -> Result<CodeObject, CompileError> {
|
||||
let parsed = parser::parse(source_code.text, parser::Mode::from(mode).into())
|
||||
let parser_mode = match mode {
|
||||
Mode::Exec => parser::Mode::Module,
|
||||
Mode::Eval => parser::Mode::Expression,
|
||||
// ruff does not have an interactive mode, which is fine,
|
||||
// since these are only different in terms of compilation
|
||||
Mode::Single | Mode::BlockExpr => parser::Mode::Module,
|
||||
};
|
||||
let parsed = parser::parse(source_code.text, parser_mode.into())
|
||||
.map_err(|err| CompileError::from_ruff_parse_error(err, &source_code))?;
|
||||
let ast = parsed.into_syntax();
|
||||
compile::compile_top(ast, source_code, mode, opts).map_err(|e| e.into())
|
||||
|
||||
@@ -494,37 +494,7 @@ impl Representable for PyComplex {
|
||||
// TODO: when you fix this, move it to rustpython_common::complex::repr and update
|
||||
// ast/src/unparse.rs + impl Display for Constant in ast/src/constant.rs
|
||||
let Complex64 { re, im } = zelf.value;
|
||||
// integer => drop ., fractional => float_ops
|
||||
let mut im_part = if im.fract() == 0.0 {
|
||||
im.to_string()
|
||||
} else {
|
||||
crate::literal::float::to_string(im)
|
||||
};
|
||||
im_part.push('j');
|
||||
|
||||
// positive empty => return im_part, integer => drop ., fractional => float_ops
|
||||
let re_part = if re == 0.0 {
|
||||
if re.is_sign_positive() {
|
||||
return Ok(im_part);
|
||||
} else {
|
||||
re.to_string()
|
||||
}
|
||||
} else if re.fract() == 0.0 {
|
||||
re.to_string()
|
||||
} else {
|
||||
crate::literal::float::to_string(re)
|
||||
};
|
||||
let mut result = String::with_capacity(
|
||||
re_part.len() + im_part.len() + 2 + im.is_sign_positive() as usize,
|
||||
);
|
||||
result.push('(');
|
||||
result.push_str(&re_part);
|
||||
if im.is_sign_positive() || im.is_nan() {
|
||||
result.push('+');
|
||||
}
|
||||
result.push_str(&im_part);
|
||||
result.push(')');
|
||||
Ok(result)
|
||||
Ok(rustpython_literal::float::complex_to_string(re, im))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::*;
|
||||
use num_traits::ToPrimitive;
|
||||
use rustpython_compiler_core::bytecode;
|
||||
|
||||
impl Node for ruff::ConversionFlag {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_code: &SourceCodeOwned) -> PyObjectRef {
|
||||
@@ -13,7 +14,13 @@ impl Node for ruff::ConversionFlag {
|
||||
) -> PyResult<Self> {
|
||||
i32::try_from_object(vm, object)?
|
||||
.to_u32()
|
||||
.and_then(ruff::ConversionFlag::from_op_arg)
|
||||
.and_then(bytecode::ConversionFlag::from_op_arg)
|
||||
.map(|flag| match flag {
|
||||
bytecode::ConversionFlag::None => Self::None,
|
||||
bytecode::ConversionFlag::Str => Self::Str,
|
||||
bytecode::ConversionFlag::Ascii => Self::Ascii,
|
||||
bytecode::ConversionFlag::Repr => Self::Repr,
|
||||
})
|
||||
.ok_or_else(|| vm.new_value_error("invalid conversion flag".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user