mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Add #[py_class] attribute proc macro
This commit is contained in:
6 files changed
+517
-278
No files matched your search
+1
-1
@@ -8,6 +8,6 @@ edition = "2018"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "0.15.29"
|
||||
syn = { version = "0.15.29", features = ["full"] }
|
||||
quote = "0.6.11"
|
||||
proc-macro2 = "0.4.27"
|
||||
+190
-8
@@ -1,21 +1,57 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::quote;
|
||||
use syn::{Data, DeriveInput, Fields};
|
||||
use syn::{
|
||||
parse_macro_input, AttributeArgs, Data, DeriveInput, Fields, Ident, ImplItem, Item, Lit, Meta,
|
||||
NestedMeta,
|
||||
};
|
||||
|
||||
#[proc_macro_derive(FromArgs)]
|
||||
fn rustpython_path(inside_vm: bool) -> syn::Path {
|
||||
let path = if inside_vm {
|
||||
quote!(crate)
|
||||
} else {
|
||||
quote!(::rustpython_vm)
|
||||
};
|
||||
syn::parse2(path).unwrap()
|
||||
}
|
||||
|
||||
/// Does the item have the #[__inside_vm] attribute on it, signifying that the derive target is
|
||||
/// being derived from inside the `rustpython_vm` crate.
|
||||
fn rustpython_path_derive(input: &DeriveInput) -> syn::Path {
|
||||
rustpython_path(
|
||||
input
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|attr| attr.path.is_ident("__inside_vm")),
|
||||
)
|
||||
}
|
||||
|
||||
fn rustpython_path_attr(attr: &AttributeArgs) -> syn::Path {
|
||||
rustpython_path(attr.iter().any(|meta| {
|
||||
if let syn::NestedMeta::Meta(meta) = meta {
|
||||
if let syn::Meta::Word(ident) = meta {
|
||||
ident == "__inside_vm"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromArgs, attributes(__inside_vm))]
|
||||
pub fn derive_from_args(input: TokenStream) -> TokenStream {
|
||||
let ast: DeriveInput = syn::parse(input).unwrap();
|
||||
|
||||
let gen = impl_from_args(&ast);
|
||||
let gen = impl_from_args(ast);
|
||||
gen.to_string().parse().unwrap()
|
||||
}
|
||||
|
||||
fn impl_from_args(input: &DeriveInput) -> TokenStream2 {
|
||||
// FIXME: This references types using `crate` instead of `rustpython_vm`
|
||||
// so that it can be used in the latter. How can we support both?
|
||||
fn impl_from_args(input: DeriveInput) -> TokenStream2 {
|
||||
let rp_path = rustpython_path_derive(&input);
|
||||
let fields = match input.data {
|
||||
Data::Struct(ref data) => {
|
||||
match data.fields {
|
||||
@@ -36,7 +72,7 @@ fn impl_from_args(input: &DeriveInput) -> TokenStream2 {
|
||||
|
||||
let name = &input.ident;
|
||||
quote! {
|
||||
impl crate::function::FromArgs for #name {
|
||||
impl #rp_path::function::FromArgs for #name {
|
||||
fn from_args(
|
||||
vm: &crate::vm::VirtualMachine,
|
||||
args: &mut crate::function::PyFuncArgs
|
||||
@@ -46,3 +82,149 @@ fn impl_from_args(input: &DeriveInput) -> TokenStream2 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn py_class(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let attr = parse_macro_input!(attr as AttributeArgs);
|
||||
let item = parse_macro_input!(item as Item);
|
||||
impl_py_class(attr, item).into()
|
||||
}
|
||||
|
||||
enum MethodKind {
|
||||
Method,
|
||||
Property,
|
||||
}
|
||||
|
||||
impl MethodKind {
|
||||
fn to_ctx_constructor_fn(&self) -> Ident {
|
||||
let f = match self {
|
||||
MethodKind::Method => "new_rustfunc",
|
||||
MethodKind::Property => "new_property",
|
||||
};
|
||||
Ident::new(f, Span::call_site())
|
||||
}
|
||||
}
|
||||
|
||||
struct Method {
|
||||
fn_name: Ident,
|
||||
py_name: String,
|
||||
kind: MethodKind,
|
||||
}
|
||||
|
||||
fn item_impl_to_methods<'a>(imp: &'a syn::ItemImpl) -> impl Iterator<Item = Method> + 'a {
|
||||
imp.items.iter().filter_map(|item| {
|
||||
if let ImplItem::Method(meth) = item {
|
||||
let mut py_name = None;
|
||||
let mut kind = MethodKind::Method;
|
||||
let metas_iter = meth
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if attr.path.is_ident("py_class") {
|
||||
let meta = attr.parse_meta().expect("Invalid attribute");
|
||||
if let Meta::List(list) = meta {
|
||||
Some(list)
|
||||
} else {
|
||||
panic!(
|
||||
"#[py_class] attribute on a method should be a list, like \
|
||||
#[py_class(...)]"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flat_map(|attr| attr.nested);
|
||||
for meta in metas_iter {
|
||||
if let NestedMeta::Meta(meta) = meta {
|
||||
match meta {
|
||||
Meta::NameValue(name_value) => {
|
||||
if name_value.ident == "name" {
|
||||
if let Lit::Str(s) = &name_value.lit {
|
||||
py_name = Some(s.value());
|
||||
} else {
|
||||
panic!("#[py_class(name = ...)] must be a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
Meta::Word(ident) => match ident.to_string().as_str() {
|
||||
"property" => kind = MethodKind::Property,
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
let py_name = py_name.unwrap_or_else(|| meth.sig.ident.to_string());
|
||||
Some(Method {
|
||||
fn_name: meth.sig.ident.clone(),
|
||||
py_name,
|
||||
kind,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn impl_py_class(attr: AttributeArgs, item: Item) -> TokenStream2 {
|
||||
let imp = if let Item::Impl(imp) = item {
|
||||
imp
|
||||
} else {
|
||||
return quote!(#item);
|
||||
};
|
||||
let rp_path = rustpython_path_attr(&attr);
|
||||
let mut class_name = None;
|
||||
let mut doc = None;
|
||||
for attr in attr {
|
||||
if let NestedMeta::Meta(meta) = attr {
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if name_value.ident == "name" {
|
||||
if let Lit::Str(s) = name_value.lit {
|
||||
class_name = Some(s.value());
|
||||
} else {
|
||||
panic!("#[py_class(name = ...)] must be a string");
|
||||
}
|
||||
} else if name_value.ident == "doc" {
|
||||
if let Lit::Str(s) = name_value.lit {
|
||||
doc = Some(s.value());
|
||||
} else {
|
||||
panic!("#[py_class(name = ...)] must be a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let class_name = class_name.expect("#[py_class] must have a name");
|
||||
let doc = match doc {
|
||||
Some(doc) => quote!(Some(#doc)),
|
||||
None => quote!(None),
|
||||
};
|
||||
let ty = &imp.self_ty;
|
||||
let methods = item_impl_to_methods(&imp).map(
|
||||
|Method {
|
||||
py_name,
|
||||
fn_name,
|
||||
kind,
|
||||
}| {
|
||||
let constructor_fn = kind.to_ctx_constructor_fn();
|
||||
quote! {
|
||||
ctx.set_attr(class, #py_name, ctx.#constructor_fn(#ty::#fn_name));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
quote! {
|
||||
#imp
|
||||
impl #rp_path::pyobject::IntoPyClass for #ty {
|
||||
const NAME: &'static str = #class_name;
|
||||
const DOC: Option<&'static str> = #doc;
|
||||
fn _extend_class(
|
||||
ctx: &#rp_path::pyobject::PyContext,
|
||||
class: &#rp_path::obj::objtype::PyClassRef,
|
||||
) {
|
||||
#(#methods)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -536,6 +536,7 @@ fn builtin_pow(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
#[derive(Debug, FromArgs)]
|
||||
#[__inside_vm]
|
||||
pub struct PrintOptions {
|
||||
sep: Option<PyStringRef>,
|
||||
end: Option<PyStringRef>,
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ extern crate statrs;
|
||||
|
||||
extern crate rustpython_parser;
|
||||
#[macro_use]
|
||||
extern crate rustpython_derive;
|
||||
pub extern crate rustpython_derive;
|
||||
|
||||
//extern crate eval; use eval::eval::*;
|
||||
// use py_code_object::{Function, NativeType, PyCodeObject};
|
||||
|
||||
+304
-268
@@ -10,8 +10,8 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
use crate::format::{FormatParseError, FormatPart, FormatString};
|
||||
use crate::function::{OptionalArg, PyFuncArgs};
|
||||
use crate::pyobject::{
|
||||
IdProtocol, IntoPyObject, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue,
|
||||
TryFromObject, TryIntoRef, TypeProtocol,
|
||||
IdProtocol, IntoPyClass, IntoPyObject, PyContext, PyIterable, PyObjectRef, PyRef, PyResult,
|
||||
PyValue, TryFromObject, TryIntoRef, TypeProtocol,
|
||||
};
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
@@ -48,80 +48,138 @@ impl TryIntoRef<PyString> for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyStringRef {
|
||||
fn add(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
if objtype::isinstance(&rhs, &vm.ctx.str_type()) {
|
||||
Ok(format!("{}{}", self.value, get_value(&rhs)))
|
||||
fn get_fill_char<'a>(rep: &'a OptionalArg<PyStringRef>, vm: &VirtualMachine) -> PyResult<&'a str> {
|
||||
let rep_str = match rep {
|
||||
OptionalArg::Present(ref st) => &st.value,
|
||||
OptionalArg::Missing => " ",
|
||||
};
|
||||
if rep_str.len() == 1 {
|
||||
Ok(rep_str)
|
||||
} else {
|
||||
Err(vm.new_type_error("The fill character must be exactly one character long".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[py_class(
|
||||
__inside_vm,
|
||||
name = "str",
|
||||
doc = "str(object='') -> str\n\
|
||||
str(bytes_or_buffer[, encoding[, errors]]) -> str\n\
|
||||
\n\
|
||||
Create a new string object from the given object. If encoding or\n\
|
||||
errors is specified, then the object must expose a data buffer\n\
|
||||
that will be decoded using the given encoding and error handler.\n\
|
||||
Otherwise, returns the result of object.__str__() (if defined)\n\
|
||||
or repr(object).\n\
|
||||
encoding defaults to sys.getdefaultencoding().\n\
|
||||
errors defaults to 'strict'."
|
||||
)]
|
||||
impl PyString {
|
||||
// TODO: should with following format
|
||||
// class str(object='')
|
||||
// class str(object=b'', encoding='utf-8', errors='strict')
|
||||
#[py_class(name = "__new__")]
|
||||
fn new(
|
||||
cls: PyClassRef,
|
||||
object: OptionalArg<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyStringRef> {
|
||||
let string = match object {
|
||||
OptionalArg::Present(ref input) => vm.to_str(input)?.into_object(),
|
||||
OptionalArg::Missing => vm.new_str("".to_string()),
|
||||
};
|
||||
if string.class().is(&cls) {
|
||||
TryFromObject::try_from_object(vm, string)
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot add {} and {}", self, rhs)))
|
||||
let payload = string.payload::<PyString>().unwrap();
|
||||
payload.clone().into_ref_with_type(vm, cls)
|
||||
}
|
||||
}
|
||||
#[py_class(name = "__add__")]
|
||||
fn add(zelf: PyStringRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
if objtype::isinstance(&rhs, &vm.ctx.str_type()) {
|
||||
Ok(format!("{}{}", zelf.value, get_value(&rhs)))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot add {} and {}", zelf, rhs)))
|
||||
}
|
||||
}
|
||||
|
||||
fn bool(self, _vm: &VirtualMachine) -> bool {
|
||||
!self.value.is_empty()
|
||||
#[py_class(name = "__bool__")]
|
||||
fn bool(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
!zelf.value.is_empty()
|
||||
}
|
||||
|
||||
fn eq(self, rhs: PyObjectRef, vm: &VirtualMachine) -> bool {
|
||||
#[py_class(name = "__eq__")]
|
||||
fn eq(zelf: PyStringRef, rhs: PyObjectRef, vm: &VirtualMachine) -> bool {
|
||||
if objtype::isinstance(&rhs, &vm.ctx.str_type()) {
|
||||
self.value == get_value(&rhs)
|
||||
zelf.value == get_value(&rhs)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(self, needle: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
self.value.contains(&needle.value)
|
||||
#[py_class(name = "__contains__")]
|
||||
fn contains(zelf: PyStringRef, needle: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
zelf.value.contains(&needle.value)
|
||||
}
|
||||
|
||||
fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
subscript(vm, &self.value, needle)
|
||||
#[py_class(name = "__getitem__")]
|
||||
fn getitem(zelf: PyStringRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
subscript(vm, &zelf.value, needle)
|
||||
}
|
||||
|
||||
fn gt(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
#[py_class(name = "__gt__")]
|
||||
fn gt(zelf: PyStringRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
if objtype::isinstance(&rhs, &vm.ctx.str_type()) {
|
||||
Ok(self.value > get_value(&rhs))
|
||||
Ok(zelf.value > get_value(&rhs))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot compare {} and {}", self, rhs)))
|
||||
Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs)))
|
||||
}
|
||||
}
|
||||
|
||||
fn ge(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
#[py_class(name = "__ge__")]
|
||||
fn ge(zelf: PyStringRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
if objtype::isinstance(&rhs, &vm.ctx.str_type()) {
|
||||
Ok(self.value >= get_value(&rhs))
|
||||
Ok(zelf.value >= get_value(&rhs))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot compare {} and {}", self, rhs)))
|
||||
Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs)))
|
||||
}
|
||||
}
|
||||
|
||||
fn lt(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
#[py_class(name = "__lt__")]
|
||||
fn lt(zelf: PyStringRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
if objtype::isinstance(&rhs, &vm.ctx.str_type()) {
|
||||
Ok(self.value < get_value(&rhs))
|
||||
Ok(zelf.value < get_value(&rhs))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot compare {} and {}", self, rhs)))
|
||||
Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs)))
|
||||
}
|
||||
}
|
||||
|
||||
fn le(self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
#[py_class(name = "__le__")]
|
||||
fn le(zelf: PyStringRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
if objtype::isinstance(&rhs, &vm.ctx.str_type()) {
|
||||
Ok(self.value <= get_value(&rhs))
|
||||
Ok(zelf.value <= get_value(&rhs))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot compare {} and {}", self, rhs)))
|
||||
Err(vm.new_type_error(format!("Cannot compare {} and {}", zelf, rhs)))
|
||||
}
|
||||
}
|
||||
|
||||
fn hash(self, _vm: &VirtualMachine) -> usize {
|
||||
#[py_class(name = "__hash__")]
|
||||
fn hash(zelf: PyStringRef, _vm: &VirtualMachine) -> usize {
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
self.value.hash(&mut hasher);
|
||||
zelf.value.hash(&mut hasher);
|
||||
hasher.finish() as usize
|
||||
}
|
||||
|
||||
fn len(self, _vm: &VirtualMachine) -> usize {
|
||||
self.value.chars().count()
|
||||
#[py_class(name = "__len__")]
|
||||
fn len(zelf: PyStringRef, _vm: &VirtualMachine) -> usize {
|
||||
zelf.value.chars().count()
|
||||
}
|
||||
|
||||
fn mul(self, val: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
#[py_class(name = "__mul__")]
|
||||
fn mul(zelf: PyStringRef, val: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
if objtype::isinstance(&val, &vm.ctx.int_type()) {
|
||||
let value = &self.value;
|
||||
let value = &zelf.value;
|
||||
let multiplier = objint::get_value(&val).to_i32().unwrap();
|
||||
let mut result = String::new();
|
||||
for _x in 0..multiplier {
|
||||
@@ -129,16 +187,18 @@ impl PyStringRef {
|
||||
}
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot multiply {} and {}", self, val)))
|
||||
Err(vm.new_type_error(format!("Cannot multiply {} and {}", zelf, val)))
|
||||
}
|
||||
}
|
||||
|
||||
fn str(self, _vm: &VirtualMachine) -> PyStringRef {
|
||||
self
|
||||
#[py_class(name = "__str__")]
|
||||
fn str(zelf: PyStringRef, _vm: &VirtualMachine) -> PyStringRef {
|
||||
zelf
|
||||
}
|
||||
|
||||
fn repr(self, _vm: &VirtualMachine) -> String {
|
||||
let value = &self.value;
|
||||
#[py_class(name = "__repr__")]
|
||||
fn repr(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
let value = &zelf.value;
|
||||
let quote_char = if count_char(value, '\'') > count_char(value, '"') {
|
||||
'"'
|
||||
} else {
|
||||
@@ -167,31 +227,31 @@ impl PyStringRef {
|
||||
formatted
|
||||
}
|
||||
|
||||
fn lower(self, _vm: &VirtualMachine) -> String {
|
||||
self.value.to_lowercase()
|
||||
fn lower(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
zelf.value.to_lowercase()
|
||||
}
|
||||
|
||||
// casefold is much more aggressive than lower
|
||||
fn casefold(self, _vm: &VirtualMachine) -> String {
|
||||
caseless::default_case_fold_str(&self.value)
|
||||
fn casefold(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
caseless::default_case_fold_str(&zelf.value)
|
||||
}
|
||||
|
||||
fn upper(self, _vm: &VirtualMachine) -> String {
|
||||
self.value.to_uppercase()
|
||||
fn upper(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
zelf.value.to_uppercase()
|
||||
}
|
||||
|
||||
fn capitalize(self, _vm: &VirtualMachine) -> String {
|
||||
let (first_part, lower_str) = self.value.split_at(1);
|
||||
fn capitalize(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
let (first_part, lower_str) = zelf.value.split_at(1);
|
||||
format!("{}{}", first_part.to_uppercase(), lower_str)
|
||||
}
|
||||
|
||||
fn split(
|
||||
self,
|
||||
pattern: OptionalArg<Self>,
|
||||
zelf: PyStringRef,
|
||||
pattern: OptionalArg<PyStringRef>,
|
||||
num: OptionalArg<usize>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyObjectRef {
|
||||
let value = &self.value;
|
||||
let value = &zelf.value;
|
||||
let pattern = match pattern {
|
||||
OptionalArg::Present(ref s) => &s.value,
|
||||
OptionalArg::Missing => " ",
|
||||
@@ -207,12 +267,12 @@ impl PyStringRef {
|
||||
}
|
||||
|
||||
fn rsplit(
|
||||
self,
|
||||
pattern: OptionalArg<Self>,
|
||||
zelf: PyStringRef,
|
||||
pattern: OptionalArg<PyStringRef>,
|
||||
num: OptionalArg<usize>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyObjectRef {
|
||||
let value = &self.value;
|
||||
let value = &zelf.value;
|
||||
let pattern = match pattern {
|
||||
OptionalArg::Present(ref s) => &s.value,
|
||||
OptionalArg::Missing => " ",
|
||||
@@ -227,85 +287,113 @@ impl PyStringRef {
|
||||
vm.ctx.new_list(elements)
|
||||
}
|
||||
|
||||
fn strip(self, _vm: &VirtualMachine) -> String {
|
||||
self.value.trim().to_string()
|
||||
fn strip(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
zelf.value.trim().to_string()
|
||||
}
|
||||
|
||||
fn lstrip(self, _vm: &VirtualMachine) -> String {
|
||||
self.value.trim_start().to_string()
|
||||
fn lstrip(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
zelf.value.trim_start().to_string()
|
||||
}
|
||||
|
||||
fn rstrip(self, _vm: &VirtualMachine) -> String {
|
||||
self.value.trim_end().to_string()
|
||||
fn rstrip(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
zelf.value.trim_end().to_string()
|
||||
}
|
||||
|
||||
fn endswith(
|
||||
self,
|
||||
zelf: PyStringRef,
|
||||
suffix: PyStringRef,
|
||||
start: OptionalArg<isize>,
|
||||
end: OptionalArg<isize>,
|
||||
_vm: &VirtualMachine,
|
||||
) -> bool {
|
||||
if let Some((start, end)) = adjust_indices(start, end, self.value.len()) {
|
||||
self.value[start..end].ends_with(&suffix.value)
|
||||
if let Some((start, end)) = adjust_indices(start, end, zelf.value.len()) {
|
||||
zelf.value[start..end].ends_with(&suffix.value)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn startswith(
|
||||
self,
|
||||
zelf: PyStringRef,
|
||||
prefix: PyStringRef,
|
||||
start: OptionalArg<isize>,
|
||||
end: OptionalArg<isize>,
|
||||
_vm: &VirtualMachine,
|
||||
) -> bool {
|
||||
if let Some((start, end)) = adjust_indices(start, end, self.value.len()) {
|
||||
self.value[start..end].starts_with(&prefix.value)
|
||||
if let Some((start, end)) = adjust_indices(start, end, zelf.value.len()) {
|
||||
zelf.value[start..end].starts_with(&prefix.value)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn isalnum(self, _vm: &VirtualMachine) -> bool {
|
||||
!self.value.is_empty() && self.value.chars().all(char::is_alphanumeric)
|
||||
fn isalnum(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
!zelf.value.is_empty() && zelf.value.chars().all(char::is_alphanumeric)
|
||||
}
|
||||
|
||||
fn isnumeric(self, _vm: &VirtualMachine) -> bool {
|
||||
!self.value.is_empty() && self.value.chars().all(char::is_numeric)
|
||||
fn isnumeric(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
!zelf.value.is_empty() && zelf.value.chars().all(char::is_numeric)
|
||||
}
|
||||
|
||||
fn isdigit(self, _vm: &VirtualMachine) -> bool {
|
||||
fn isdigit(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
// python's isdigit also checks if exponents are digits, these are the unicodes for exponents
|
||||
let valid_unicodes: [u16; 10] = [
|
||||
0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079,
|
||||
];
|
||||
|
||||
if self.value.is_empty() {
|
||||
if zelf.value.is_empty() {
|
||||
false
|
||||
} else {
|
||||
self.value
|
||||
zelf.value
|
||||
.chars()
|
||||
.filter(|c| !c.is_digit(10))
|
||||
.all(|c| valid_unicodes.contains(&(c as u16)))
|
||||
}
|
||||
}
|
||||
|
||||
fn isdecimal(self, _vm: &VirtualMachine) -> bool {
|
||||
if self.value.is_empty() {
|
||||
fn isdecimal(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
if zelf.value.is_empty() {
|
||||
false
|
||||
} else {
|
||||
self.value.chars().all(|c| c.is_ascii_digit())
|
||||
zelf.value.chars().all(|c| c.is_ascii_digit())
|
||||
}
|
||||
}
|
||||
|
||||
fn title(self, _vm: &VirtualMachine) -> String {
|
||||
make_title(&self.value)
|
||||
fn format(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
if args.args.is_empty() {
|
||||
return Err(vm.new_type_error(
|
||||
"descriptor 'format' of 'str' object needs an argument".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let zelf = &args.args[0];
|
||||
if !objtype::isinstance(&zelf, &vm.ctx.str_type()) {
|
||||
let zelf_typ = zelf.class();
|
||||
let actual_type = vm.to_pystr(&zelf_typ)?;
|
||||
return Err(vm.new_type_error(format!(
|
||||
"descriptor 'format' requires a 'str' object but received a '{}'",
|
||||
actual_type
|
||||
)));
|
||||
}
|
||||
let format_string_text = get_value(zelf);
|
||||
match FormatString::from_str(format_string_text.as_str()) {
|
||||
Ok(format_string) => perform_format(vm, &format_string, &args),
|
||||
Err(err) => match err {
|
||||
FormatParseError::UnmatchedBracket => {
|
||||
Err(vm.new_value_error("expected '}' before end of string".to_string()))
|
||||
}
|
||||
_ => Err(vm.new_value_error("Unexpected error parsing format string".to_string())),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn swapcase(self, _vm: &VirtualMachine) -> String {
|
||||
let mut swapped_str = String::with_capacity(self.value.len());
|
||||
for c in self.value.chars() {
|
||||
fn title(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
make_title(&zelf.value)
|
||||
}
|
||||
|
||||
fn swapcase(zelf: PyStringRef, _vm: &VirtualMachine) -> String {
|
||||
let mut swapped_str = String::with_capacity(zelf.value.len());
|
||||
for c in zelf.value.chars() {
|
||||
// to_uppercase returns an iterator, to_ascii_uppercase returns the char
|
||||
if c.is_lowercase() {
|
||||
swapped_str.push(c.to_ascii_uppercase());
|
||||
@@ -318,54 +406,54 @@ impl PyStringRef {
|
||||
swapped_str
|
||||
}
|
||||
|
||||
fn isalpha(self, _vm: &VirtualMachine) -> bool {
|
||||
!self.value.is_empty() && self.value.chars().all(char::is_alphanumeric)
|
||||
fn isalpha(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
!zelf.value.is_empty() && zelf.value.chars().all(char::is_alphanumeric)
|
||||
}
|
||||
|
||||
fn replace(
|
||||
self,
|
||||
old: Self,
|
||||
new: Self,
|
||||
zelf: PyStringRef,
|
||||
old: PyStringRef,
|
||||
new: PyStringRef,
|
||||
num: OptionalArg<usize>,
|
||||
_vm: &VirtualMachine,
|
||||
) -> String {
|
||||
match num.into_option() {
|
||||
Some(num) => self.value.replacen(&old.value, &new.value, num),
|
||||
None => self.value.replace(&old.value, &new.value),
|
||||
Some(num) => zelf.value.replacen(&old.value, &new.value, num),
|
||||
None => zelf.value.replace(&old.value, &new.value),
|
||||
}
|
||||
}
|
||||
|
||||
// cpython's isspace ignores whitespace, including \t and \n, etc, unless the whole string is empty
|
||||
// which is why isspace is using is_ascii_whitespace. Same for isupper & islower
|
||||
fn isspace(self, _vm: &VirtualMachine) -> bool {
|
||||
!self.value.is_empty() && self.value.chars().all(|c| c.is_ascii_whitespace())
|
||||
fn isspace(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
!zelf.value.is_empty() && zelf.value.chars().all(|c| c.is_ascii_whitespace())
|
||||
}
|
||||
|
||||
fn isupper(self, _vm: &VirtualMachine) -> bool {
|
||||
!self.value.is_empty()
|
||||
&& self
|
||||
fn isupper(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
!zelf.value.is_empty()
|
||||
&& zelf
|
||||
.value
|
||||
.chars()
|
||||
.filter(|x| !x.is_ascii_whitespace())
|
||||
.all(char::is_uppercase)
|
||||
}
|
||||
|
||||
fn islower(self, _vm: &VirtualMachine) -> bool {
|
||||
!self.value.is_empty()
|
||||
&& self
|
||||
fn islower(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
!zelf.value.is_empty()
|
||||
&& zelf
|
||||
.value
|
||||
.chars()
|
||||
.filter(|x| !x.is_ascii_whitespace())
|
||||
.all(char::is_lowercase)
|
||||
}
|
||||
|
||||
fn isascii(self, _vm: &VirtualMachine) -> bool {
|
||||
!self.value.is_empty() && self.value.chars().all(|c| c.is_ascii())
|
||||
fn isascii(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
!zelf.value.is_empty() && zelf.value.chars().all(|c| c.is_ascii())
|
||||
}
|
||||
|
||||
// doesn't implement keep new line delimiter just yet
|
||||
fn splitlines(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let elements = self
|
||||
fn splitlines(zelf: PyStringRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let elements = zelf
|
||||
.value
|
||||
.split('\n')
|
||||
.map(|e| vm.ctx.new_str(e.to_string()))
|
||||
@@ -373,13 +461,17 @@ impl PyStringRef {
|
||||
vm.ctx.new_list(elements)
|
||||
}
|
||||
|
||||
fn join(self, iterable: PyIterable<PyStringRef>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn join(
|
||||
zelf: PyStringRef,
|
||||
iterable: PyIterable<PyStringRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<String> {
|
||||
let mut joined = String::new();
|
||||
|
||||
for (idx, elem) in iterable.iter(vm)?.enumerate() {
|
||||
let elem = elem?;
|
||||
if idx != 0 {
|
||||
joined.push_str(&self.value);
|
||||
joined.push_str(&zelf.value);
|
||||
}
|
||||
joined.push_str(&elem.value)
|
||||
}
|
||||
@@ -388,13 +480,13 @@ impl PyStringRef {
|
||||
}
|
||||
|
||||
fn find(
|
||||
self,
|
||||
sub: Self,
|
||||
zelf: PyStringRef,
|
||||
sub: PyStringRef,
|
||||
start: OptionalArg<isize>,
|
||||
end: OptionalArg<isize>,
|
||||
_vm: &VirtualMachine,
|
||||
) -> isize {
|
||||
let value = &self.value;
|
||||
let value = &zelf.value;
|
||||
if let Some((start, end)) = adjust_indices(start, end, value.len()) {
|
||||
match value[start..end].find(&sub.value) {
|
||||
Some(num) => (start + num) as isize,
|
||||
@@ -406,13 +498,13 @@ impl PyStringRef {
|
||||
}
|
||||
|
||||
fn rfind(
|
||||
self,
|
||||
sub: Self,
|
||||
zelf: PyStringRef,
|
||||
sub: PyStringRef,
|
||||
start: OptionalArg<isize>,
|
||||
end: OptionalArg<isize>,
|
||||
_vm: &VirtualMachine,
|
||||
) -> isize {
|
||||
let value = &self.value;
|
||||
let value = &zelf.value;
|
||||
if let Some((start, end)) = adjust_indices(start, end, value.len()) {
|
||||
match value[start..end].rfind(&sub.value) {
|
||||
Some(num) => (start + num) as isize,
|
||||
@@ -424,13 +516,13 @@ impl PyStringRef {
|
||||
}
|
||||
|
||||
fn index(
|
||||
self,
|
||||
sub: Self,
|
||||
zelf: PyStringRef,
|
||||
sub: PyStringRef,
|
||||
start: OptionalArg<isize>,
|
||||
end: OptionalArg<isize>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<usize> {
|
||||
let value = &self.value;
|
||||
let value = &zelf.value;
|
||||
if let Some((start, end)) = adjust_indices(start, end, value.len()) {
|
||||
match value[start..end].find(&sub.value) {
|
||||
Some(num) => Ok(start + num),
|
||||
@@ -442,13 +534,13 @@ impl PyStringRef {
|
||||
}
|
||||
|
||||
fn rindex(
|
||||
self,
|
||||
sub: Self,
|
||||
zelf: PyStringRef,
|
||||
sub: PyStringRef,
|
||||
start: OptionalArg<isize>,
|
||||
end: OptionalArg<isize>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<usize> {
|
||||
let value = &self.value;
|
||||
let value = &zelf.value;
|
||||
if let Some((start, end)) = adjust_indices(start, end, value.len()) {
|
||||
match value[start..end].rfind(&sub.value) {
|
||||
Some(num) => Ok(start + num),
|
||||
@@ -459,8 +551,8 @@ impl PyStringRef {
|
||||
}
|
||||
}
|
||||
|
||||
fn partition(self, sub: PyStringRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let value = &self.value;
|
||||
fn partition(zelf: PyStringRef, sub: PyStringRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let value = &zelf.value;
|
||||
let sub = &sub.value;
|
||||
let mut new_tup = Vec::new();
|
||||
if value.contains(sub) {
|
||||
@@ -477,8 +569,8 @@ impl PyStringRef {
|
||||
vm.ctx.new_tuple(new_tup)
|
||||
}
|
||||
|
||||
fn rpartition(self, sub: PyStringRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let value = &self.value;
|
||||
fn rpartition(zelf: PyStringRef, sub: PyStringRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let value = &zelf.value;
|
||||
let sub = &sub.value;
|
||||
let mut new_tup = Vec::new();
|
||||
if value.contains(sub) {
|
||||
@@ -496,31 +588,31 @@ impl PyStringRef {
|
||||
vm.ctx.new_tuple(new_tup)
|
||||
}
|
||||
|
||||
fn istitle(self, _vm: &VirtualMachine) -> bool {
|
||||
if self.value.is_empty() {
|
||||
fn istitle(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
if zelf.value.is_empty() {
|
||||
false
|
||||
} else {
|
||||
self.value.split(' ').all(|word| word == make_title(word))
|
||||
zelf.value.split(' ').all(|word| word == make_title(word))
|
||||
}
|
||||
}
|
||||
|
||||
fn count(
|
||||
self,
|
||||
sub: Self,
|
||||
zelf: PyStringRef,
|
||||
sub: PyStringRef,
|
||||
start: OptionalArg<isize>,
|
||||
end: OptionalArg<isize>,
|
||||
_vm: &VirtualMachine,
|
||||
) -> usize {
|
||||
let value = &self.value;
|
||||
let value = &zelf.value;
|
||||
if let Some((start, end)) = adjust_indices(start, end, value.len()) {
|
||||
self.value[start..end].matches(&sub.value).count()
|
||||
zelf.value[start..end].matches(&sub.value).count()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn zfill(self, len: usize, _vm: &VirtualMachine) -> String {
|
||||
let value = &self.value;
|
||||
fn zfill(zelf: PyStringRef, len: usize, _vm: &VirtualMachine) -> String {
|
||||
let value = &zelf.value;
|
||||
if len <= value.len() {
|
||||
value.to_string()
|
||||
} else {
|
||||
@@ -528,35 +620,36 @@ impl PyStringRef {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fill_char<'a>(rep: &'a OptionalArg<Self>, vm: &VirtualMachine) -> PyResult<&'a str> {
|
||||
let rep_str = match rep {
|
||||
OptionalArg::Present(ref st) => &st.value,
|
||||
OptionalArg::Missing => " ",
|
||||
};
|
||||
if rep_str.len() == 1 {
|
||||
Ok(rep_str)
|
||||
} else {
|
||||
Err(vm.new_type_error(
|
||||
"The fill character must be exactly one character long".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn ljust(self, len: usize, rep: OptionalArg<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let value = &self.value;
|
||||
let rep_char = PyStringRef::get_fill_char(&rep, vm)?;
|
||||
fn ljust(
|
||||
zelf: PyStringRef,
|
||||
len: usize,
|
||||
rep: OptionalArg<PyStringRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<String> {
|
||||
let value = &zelf.value;
|
||||
let rep_char = get_fill_char(&rep, vm)?;
|
||||
Ok(format!("{}{}", value, rep_char.repeat(len)))
|
||||
}
|
||||
|
||||
fn rjust(self, len: usize, rep: OptionalArg<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let value = &self.value;
|
||||
let rep_char = PyStringRef::get_fill_char(&rep, vm)?;
|
||||
fn rjust(
|
||||
zelf: PyStringRef,
|
||||
len: usize,
|
||||
rep: OptionalArg<PyStringRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<String> {
|
||||
let value = &zelf.value;
|
||||
let rep_char = get_fill_char(&rep, vm)?;
|
||||
Ok(format!("{}{}", rep_char.repeat(len), value))
|
||||
}
|
||||
|
||||
fn center(self, len: usize, rep: OptionalArg<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let value = &self.value;
|
||||
let rep_char = PyStringRef::get_fill_char(&rep, vm)?;
|
||||
fn center(
|
||||
zelf: PyStringRef,
|
||||
len: usize,
|
||||
rep: OptionalArg<PyStringRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<String> {
|
||||
let value = &zelf.value;
|
||||
let rep_char = get_fill_char(&rep, vm)?;
|
||||
let left_buff: usize = (len - value.len()) / 2;
|
||||
let right_buff = len - value.len() - left_buff;
|
||||
Ok(format!(
|
||||
@@ -567,12 +660,12 @@ impl PyStringRef {
|
||||
))
|
||||
}
|
||||
|
||||
fn expandtabs(self, tab_stop: OptionalArg<usize>, _vm: &VirtualMachine) -> String {
|
||||
fn expandtabs(zelf: PyStringRef, tab_stop: OptionalArg<usize>, _vm: &VirtualMachine) -> String {
|
||||
let tab_stop = tab_stop.into_option().unwrap_or(8 as usize);
|
||||
let mut expanded_str = String::new();
|
||||
let mut tab_size = tab_stop;
|
||||
let mut col_count = 0 as usize;
|
||||
for ch in self.value.chars() {
|
||||
for ch in zelf.value.chars() {
|
||||
// 0x0009 is tab
|
||||
if ch == 0x0009 as char {
|
||||
let num_spaces = tab_size - col_count;
|
||||
@@ -590,8 +683,8 @@ impl PyStringRef {
|
||||
expanded_str
|
||||
}
|
||||
|
||||
fn isidentifier(self, _vm: &VirtualMachine) -> bool {
|
||||
let value = &self.value;
|
||||
fn isidentifier(zelf: PyStringRef, _vm: &VirtualMachine) -> bool {
|
||||
let value = &zelf.value;
|
||||
// a string is not an identifier if it has whitespace or starts with a number
|
||||
if !value.chars().any(|c| c.is_ascii_whitespace())
|
||||
&& !value.chars().nth(0).unwrap().is_digit(10)
|
||||
@@ -623,75 +716,66 @@ impl IntoPyObject for String {
|
||||
#[rustfmt::skip] // to avoid line splitting
|
||||
pub fn init(context: &PyContext) {
|
||||
let str_type = &context.str_type;
|
||||
let str_doc = "str(object='') -> str\n\
|
||||
str(bytes_or_buffer[, encoding[, errors]]) -> str\n\
|
||||
\n\
|
||||
Create a new string object from the given object. If encoding or\n\
|
||||
errors is specified, then the object must expose a data buffer\n\
|
||||
that will be decoded using the given encoding and error handler.\n\
|
||||
Otherwise, returns the result of object.__str__() (if defined)\n\
|
||||
or repr(object).\n\
|
||||
encoding defaults to sys.getdefaultencoding().\n\
|
||||
errors defaults to 'strict'.";
|
||||
|
||||
extend_class!(context, str_type, {
|
||||
"__add__" => context.new_rustfunc(PyStringRef::add),
|
||||
"__bool__" => context.new_rustfunc(PyStringRef::bool),
|
||||
"__contains__" => context.new_rustfunc(PyStringRef::contains),
|
||||
"__doc__" => context.new_str(str_doc.to_string()),
|
||||
"__eq__" => context.new_rustfunc(PyStringRef::eq),
|
||||
"__ge__" => context.new_rustfunc(PyStringRef::ge),
|
||||
"__getitem__" => context.new_rustfunc(PyStringRef::getitem),
|
||||
"__gt__" => context.new_rustfunc(PyStringRef::gt),
|
||||
"__hash__" => context.new_rustfunc(PyStringRef::hash),
|
||||
"__lt__" => context.new_rustfunc(PyStringRef::lt),
|
||||
"__le__" => context.new_rustfunc(PyStringRef::le),
|
||||
"__len__" => context.new_rustfunc(PyStringRef::len),
|
||||
"__mul__" => context.new_rustfunc(PyStringRef::mul),
|
||||
"__new__" => context.new_rustfunc(str_new),
|
||||
"__repr__" => context.new_rustfunc(PyStringRef::repr),
|
||||
"__str__" => context.new_rustfunc(PyStringRef::str),
|
||||
"capitalize" => context.new_rustfunc(PyStringRef::capitalize),
|
||||
"casefold" => context.new_rustfunc(PyStringRef::casefold),
|
||||
"center" => context.new_rustfunc(PyStringRef::center),
|
||||
"count" => context.new_rustfunc(PyStringRef::count),
|
||||
"endswith" => context.new_rustfunc(PyStringRef::endswith),
|
||||
"expandtabs" => context.new_rustfunc(PyStringRef::expandtabs),
|
||||
"find" => context.new_rustfunc(PyStringRef::find),
|
||||
"format" => context.new_rustfunc(str_format),
|
||||
"index" => context.new_rustfunc(PyStringRef::index),
|
||||
"isalnum" => context.new_rustfunc(PyStringRef::isalnum),
|
||||
"isalpha" => context.new_rustfunc(PyStringRef::isalpha),
|
||||
"isascii" => context.new_rustfunc(PyStringRef::isascii),
|
||||
"isdecimal" => context.new_rustfunc(PyStringRef::isdecimal),
|
||||
"isdigit" => context.new_rustfunc(PyStringRef::isdigit),
|
||||
"isidentifier" => context.new_rustfunc(PyStringRef::isidentifier),
|
||||
"islower" => context.new_rustfunc(PyStringRef::islower),
|
||||
"isnumeric" => context.new_rustfunc(PyStringRef::isnumeric),
|
||||
"isspace" => context.new_rustfunc(PyStringRef::isspace),
|
||||
"isupper" => context.new_rustfunc(PyStringRef::isupper),
|
||||
"istitle" => context.new_rustfunc(PyStringRef::istitle),
|
||||
"join" => context.new_rustfunc(PyStringRef::join),
|
||||
"lower" => context.new_rustfunc(PyStringRef::lower),
|
||||
"ljust" => context.new_rustfunc(PyStringRef::ljust),
|
||||
"lstrip" => context.new_rustfunc(PyStringRef::lstrip),
|
||||
"partition" => context.new_rustfunc(PyStringRef::partition),
|
||||
"replace" => context.new_rustfunc(PyStringRef::replace),
|
||||
"rfind" => context.new_rustfunc(PyStringRef::rfind),
|
||||
"rindex" => context.new_rustfunc(PyStringRef::rindex),
|
||||
"rjust" => context.new_rustfunc(PyStringRef::rjust),
|
||||
"rpartition" => context.new_rustfunc(PyStringRef::rpartition),
|
||||
"rsplit" => context.new_rustfunc(PyStringRef::rsplit),
|
||||
"rstrip" => context.new_rustfunc(PyStringRef::rstrip),
|
||||
"split" => context.new_rustfunc(PyStringRef::split),
|
||||
"splitlines" => context.new_rustfunc(PyStringRef::splitlines),
|
||||
"startswith" => context.new_rustfunc(PyStringRef::startswith),
|
||||
"strip" => context.new_rustfunc(PyStringRef::strip),
|
||||
"swapcase" => context.new_rustfunc(PyStringRef::swapcase),
|
||||
"title" => context.new_rustfunc(PyStringRef::title),
|
||||
"upper" => context.new_rustfunc(PyStringRef::upper),
|
||||
"zfill" => context.new_rustfunc(PyStringRef::zfill),
|
||||
});
|
||||
PyString::extend_class(context, str_type);
|
||||
// extend_class!(context, str_type, {
|
||||
// "__add__" => context.new_rustfunc(PyStringRef::add),
|
||||
// "__bool__" => context.new_rustfunc(PyStringRef::bool),
|
||||
// "__contains__" => context.new_rustfunc(PyStringRef::contains),
|
||||
// "__doc__" => context.new_str(str_doc.to_string()),
|
||||
// "__eq__" => context.new_rustfunc(PyStringRef::eq),
|
||||
// "__ge__" => context.new_rustfunc(PyStringRef::ge),
|
||||
// "__getitem__" => context.new_rustfunc(PyStringRef::getitem),
|
||||
// "__gt__" => context.new_rustfunc(PyStringRef::gt),
|
||||
// "__hash__" => context.new_rustfunc(PyStringRef::hash),
|
||||
// "__lt__" => context.new_rustfunc(PyStringRef::lt),
|
||||
// "__le__" => context.new_rustfunc(PyStringRef::le),
|
||||
// "__len__" => context.new_rustfunc(PyStringRef::len),
|
||||
// "__mul__" => context.new_rustfunc(PyStringRef::mul),
|
||||
// "__new__" => context.new_rustfunc(str_new),
|
||||
// "__repr__" => context.new_rustfunc(PyStringRef::repr),
|
||||
// "__str__" => context.new_rustfunc(PyStringRef::str),
|
||||
// "capitalize" => context.new_rustfunc(PyStringRef::capitalize),
|
||||
// "casefold" => context.new_rustfunc(PyStringRef::casefold),
|
||||
// "center" => context.new_rustfunc(PyStringRef::center),
|
||||
// "count" => context.new_rustfunc(PyStringRef::count),
|
||||
// "endswith" => context.new_rustfunc(PyStringRef::endswith),
|
||||
// "expandtabs" => context.new_rustfunc(PyStringRef::expandtabs),
|
||||
// "find" => context.new_rustfunc(PyStringRef::find),
|
||||
// "format" => context.new_rustfunc(str_format),
|
||||
// "index" => context.new_rustfunc(PyStringRef::index),
|
||||
// "isalnum" => context.new_rustfunc(PyStringRef::isalnum),
|
||||
// "isalpha" => context.new_rustfunc(PyStringRef::isalpha),
|
||||
// "isascii" => context.new_rustfunc(PyStringRef::isascii),
|
||||
// "isdecimal" => context.new_rustfunc(PyStringRef::isdecimal),
|
||||
// "isdigit" => context.new_rustfunc(PyStringRef::isdigit),
|
||||
// "isidentifier" => context.new_rustfunc(PyStringRef::isidentifier),
|
||||
// "islower" => context.new_rustfunc(PyStringRef::islower),
|
||||
// "isnumeric" => context.new_rustfunc(PyStringRef::isnumeric),
|
||||
// "isspace" => context.new_rustfunc(PyStringRef::isspace),
|
||||
// "isupper" => context.new_rustfunc(PyStringRef::isupper),
|
||||
// "istitle" => context.new_rustfunc(PyStringRef::istitle),
|
||||
// "join" => context.new_rustfunc(PyStringRef::join),
|
||||
// "lower" => context.new_rustfunc(PyStringRef::lower),
|
||||
// "ljust" => context.new_rustfunc(PyStringRef::ljust),
|
||||
// "lstrip" => context.new_rustfunc(PyStringRef::lstrip),
|
||||
// "partition" => context.new_rustfunc(PyStringRef::partition),
|
||||
// "replace" => context.new_rustfunc(PyStringRef::replace),
|
||||
// "rfind" => context.new_rustfunc(PyStringRef::rfind),
|
||||
// "rindex" => context.new_rustfunc(PyStringRef::rindex),
|
||||
// "rjust" => context.new_rustfunc(PyStringRef::rjust),
|
||||
// "rpartition" => context.new_rustfunc(PyStringRef::rpartition),
|
||||
// "rsplit" => context.new_rustfunc(PyStringRef::rsplit),
|
||||
// "rstrip" => context.new_rustfunc(PyStringRef::rstrip),
|
||||
// "split" => context.new_rustfunc(PyStringRef::split),
|
||||
// "splitlines" => context.new_rustfunc(PyStringRef::splitlines),
|
||||
// "startswith" => context.new_rustfunc(PyStringRef::startswith),
|
||||
// "strip" => context.new_rustfunc(PyStringRef::strip),
|
||||
// "swapcase" => context.new_rustfunc(PyStringRef::swapcase),
|
||||
// "title" => context.new_rustfunc(PyStringRef::title),
|
||||
// "upper" => context.new_rustfunc(PyStringRef::upper),
|
||||
// "zfill" => context.new_rustfunc(PyStringRef::zfill),
|
||||
// });
|
||||
}
|
||||
|
||||
pub fn get_value(obj: &PyObjectRef) -> String {
|
||||
@@ -706,34 +790,6 @@ fn count_char(s: &str, c: char) -> usize {
|
||||
s.chars().filter(|x| *x == c).count()
|
||||
}
|
||||
|
||||
fn str_format(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
if args.args.is_empty() {
|
||||
return Err(
|
||||
vm.new_type_error("descriptor 'format' of 'str' object needs an argument".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
let zelf = &args.args[0];
|
||||
if !objtype::isinstance(&zelf, &vm.ctx.str_type()) {
|
||||
let zelf_typ = zelf.class();
|
||||
let actual_type = vm.to_pystr(&zelf_typ)?;
|
||||
return Err(vm.new_type_error(format!(
|
||||
"descriptor 'format' requires a 'str' object but received a '{}'",
|
||||
actual_type
|
||||
)));
|
||||
}
|
||||
let format_string_text = get_value(zelf);
|
||||
match FormatString::from_str(format_string_text.as_str()) {
|
||||
Ok(format_string) => perform_format(vm, &format_string, &args),
|
||||
Err(err) => match err {
|
||||
FormatParseError::UnmatchedBracket => {
|
||||
Err(vm.new_value_error("expected '}' before end of string".to_string()))
|
||||
}
|
||||
_ => Err(vm.new_value_error("Unexpected error parsing format string".to_string())),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn call_object_format(vm: &VirtualMachine, argument: PyObjectRef, format_spec: &str) -> PyResult {
|
||||
let returned_type = vm.ctx.new_str(format_spec.to_string());
|
||||
let result = vm.call_method(&argument, "__format__", vec![returned_type])?;
|
||||
@@ -797,26 +853,6 @@ fn perform_format(
|
||||
Ok(vm.ctx.new_str(final_string))
|
||||
}
|
||||
|
||||
// TODO: should with following format
|
||||
// class str(object='')
|
||||
// class str(object=b'', encoding='utf-8', errors='strict')
|
||||
fn str_new(
|
||||
cls: PyClassRef,
|
||||
object: OptionalArg<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyStringRef> {
|
||||
let string = match object {
|
||||
OptionalArg::Present(ref input) => vm.to_str(input)?.into_object(),
|
||||
OptionalArg::Missing => vm.new_str("".to_string()),
|
||||
};
|
||||
if string.class().is(&cls) {
|
||||
TryFromObject::try_from_object(vm, string)
|
||||
} else {
|
||||
let payload = string.payload::<PyString>().unwrap();
|
||||
payload.clone().into_ref_with_type(vm, cls)
|
||||
}
|
||||
}
|
||||
|
||||
impl PySliceableSequence for String {
|
||||
fn do_slice(&self, range: Range<usize>) -> Self {
|
||||
to_graphemes(self)
|
||||
|
||||
@@ -1259,6 +1259,26 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoPyClass {
|
||||
const NAME: &'static str;
|
||||
const DOC: Option<&'static str> = None;
|
||||
|
||||
fn _extend_class(ctx: &PyContext, class: &PyClassRef);
|
||||
|
||||
fn extend_class(ctx: &PyContext, class: &PyClassRef) {
|
||||
Self::_extend_class(ctx, class);
|
||||
if let Some(doc) = Self::DOC {
|
||||
ctx.set_attr(class, "__doc__", ctx.new_str(doc.into()));
|
||||
}
|
||||
}
|
||||
|
||||
fn make_class(ctx: &PyContext) -> PyClassRef {
|
||||
let py_class = ctx.new_class(Self::NAME, ctx.object());
|
||||
Self::extend_class(ctx, &py_class);
|
||||
py_class
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in new issue
Block a user